Por favor, ¿alguien podría sugerirme cómo hacerlo?
Tengo dos listas (ambas con sha1sums y sus nombres de archivo relativos) pero con un formato diferente, aquí hay un ejemplo:
lista01.txt
artist'ssomesong.mp3,3f1dfd39e88e00477483dfd578d5284f5490a0a5
hello(previous one).sh,55a5fdde4843fc2f9d9e691cb658b6389d698b22
mymovie [1989, director's cut].mov,4bdee0fc0eb7a3dbc5bbe2b65a02a1f9dc76c443
[etc...]
lista02.txt
3f1dfd39e88e00477483dfd578d5284f5490a0a5 /path/to/my new music/album.wav
f77921adf6748f65fe688a5484ed901d4g9932hh /path/to/movies/[YEAR]/mymovie [1989, director's cut].mov
55a5fdde4843fc2f9d9e691cb658b6389d698b22 /path/to/scripts,regexs/hello(previous one).sh
[etc...]
Como puede ver, la única entrada buena es sha1sum 55a5fdde4843fc2f9d9e691cb658b6389d698b22
con nombre de archivo hello(previous one).sh
(segunda línea list01.txt
y tercera línea list02.txt
).
Los nombres de archivo y las rutas pueden contener espacios en blanco y caracteres especiales (por ejemplo: ' " [ ] ( ) { } y así sucesivamente...).
Lo único de lo que podemos estar 100% seguros es que list01.txt
siempre tendrá el formato ,sha1sum
; y list02.txt
siempre tendrá sha1sum /
(dos espacios antes de /).
Como en el título de esta pregunta, me gustaría utilizar uncondición-sien un script bash que verifica ambas listas para encontrar coincidencias (VERDADERO es siTanto sha1sum como filename son iguales) y cuando los encuentre, copiará cada ocurrencia usando
cp $source $destination
source=reads the /path/to/filename from list02.txt
destination=/wherever/i/want/
¡Gracias!
Respuesta1
Supuestos:
xargs
Hay herramientas GNU presentes (opciones y no estándarcp
, posiblemente separador NUL que no funcione en otrosawk
)- La longitud del hash es siempre de 40 caracteres.
- Siempre hay dos caracteres de espacio que separan el hash y la ruta del archivo en
list02.txt
- No
|
hay caracteres de barra vertical en ambos archivos (de lo contrario, utilice un separador diferente)
Primer paso, fusionar ambos archivos:
join -t'|' -1 2 \
<(sed -E 's/,(.{40})$/|\1/' list01.txt | sort -t'|' -k2) \
<(sed -E 's/(.{40}) /\1|/' list02.txt | sort -t'|' -k1)
- Primer archivo: sustituya el separador
,
por|
y ordene el archivo en el segundo campo - Segundo archivo: sustituya el separador
(dos espacios) por
|
el primer campo y ordene - Unir los archivos en el campo hash
Producción:
3f1dfd39e88e00477483dfd578d5284f5490a0a5|artist'ssomesong.mp3|/path/to/my new music/album.wav
55a5fdde4843fc2f9d9e691cb658b6389d698b22|hello(previous one).sh|/path/to/scripts,regexs/hello(previous one).sh
Luego utilícelo awk
para probar si el nombre de archivo del campo2 está presente como nombre de archivo en el último campo. Si es verdadero, imprima el último campo con un separador NUL y canalice el resultado para xargs
copiar los archivos al directorio de destino.
join -t'|' -1 2 \
<(sed -E 's/,(.{40})$/|\1/' list01.txt | sort -t'|' -k2) \
<(sed -E 's/(.{40}) /\1|/' list02.txt | sort -t'|' -k1) \
| awk -F '|' '
{
fname1=$2; sub(/.*\//, "", fname1) # extract filename1
fname2=$3; sub(/.*\//, "", fname2) # extract filename2
}
fname1 == fname2{ printf $3 "\0" } # compare filenames, print filepath with NUL separator
' | xargs -r0 cp -n -t /path/to/destination
Opciones de copia:
-n
no sobrescribir archivos existentes-t
directorio de destino
Como guión:
#!/bin/bash
join -t'|' -1 2 \
<(sed -E 's/,(.{40})$/|\1/' "$1" | sort -t'|' -k2) \
<(sed -E 's/(.{40}) /\1|/' "$2" | sort -t'|' -k1) \
| awk -F '|' '
{
fname1=$2; sub(/.*\//, "", fname1) # extract filename1
fname2=$3; sub(/.*\//, "", fname2) # extract filename2
}
fname1 == fname2{ printf $3 "\0" } # compare filenames, print filepath with NUL separator
' | xargs -r0 cp -n -t "$3"
Ejecútelo como:
./script.sh list1 list2 /path/to/destination