Por favor, alguém poderia me sugerir como fazer?
Eu tenho duas listas (ambas com sha1sums e seus nomes de arquivos relativos), mas formatadas de forma diferente, aqui está um exemplo:
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 você pode ver, a única entrada boa é para sha1sum 55a5fdde4843fc2f9d9e691cb658b6389d698b22
com nome de arquivo hello(previous one).sh
(2ª linha list01.txt
e 3ª linha list02.txt
).
Nomes de arquivos e caminhos podem conter espaços em branco e caracteres especiais (ex.: ' " [ ] ( ) { } e assim por diante...).
A única coisa para ter 100% de certeza é que list01.txt
é sempre formatado como ,sha1sum
; e list02.txt
sempre terá sha1sum /
(dois espaços antes de /).
Como no título desta pergunta, gostaria de usar umse-condiçãoem um script bash que verifica ambas as listas para encontrar correspondências (TRUE é setanto sha1sum quanto filename são iguais) e quando os encontrar, copiará todas as ocorrências usando
cp $source $destination
source=reads the /path/to/filename from list02.txt
destination=/wherever/i/want/
Obrigado!
Responder1
Premissas:
- Ferramentas GNU estão presentes (não padrão
xargs
ecp
opções, possivelmente separador NUL não funcional em outrosawk
) - O comprimento do hash é sempre de 40 caracteres
- Sempre há dois caracteres de espaço separando o hash e o caminho do arquivo em
list02.txt
- Nenhum
|
caractere de barra vertical está presente em ambos os arquivos (caso contrário, use um separador diferente)
Primeiro passo, mescle os dois arquivos:
join -t'|' -1 2 \
<(sed -E 's/,(.{40})$/|\1/' list01.txt | sort -t'|' -k2) \
<(sed -E 's/(.{40}) /\1|/' list02.txt | sort -t'|' -k1)
- Primeiro arquivo: substitua o separador
,
por|
e classifique o arquivo no segundo campo - Segundo arquivo: substitua o separador
(dois espaços) por
|
e classifique no primeiro campo - Junte os arquivos no campo hash
Saída:
3f1dfd39e88e00477483dfd578d5284f5490a0a5|artist'ssomesong.mp3|/path/to/my new music/album.wav
55a5fdde4843fc2f9d9e691cb658b6389d698b22|hello(previous one).sh|/path/to/scripts,regexs/hello(previous one).sh
Em seguida, use awk
para testar se o nome do arquivo do campo2 está presente como nome do arquivo no último campo. Se for verdade, imprima o último campo com um separador NUL e canalize o resultado para xargs
copiar os arquivos para o diretório 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
Opções de cópia:
-n
não sobrescreva arquivos existentes-t
diretório de destino
Como roteiro:
#!/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"
Execute-o como:
./script.sh list1 list2 /path/to/destination