find | xargs shasum cria a soma de verificação do próprio arquivo de soma de verificação (prematuramente) e falha ao verificar

10

Meu problema (em um script com #!/bin/sh ) é o seguinte: Eu tento verificar todos os arquivos em um diretório para fins de arquivamento. O arquivo de soma de verificação (no meu caso, sha1) com todos os nomes de arquivos deve residir no mesmo diretório. Vamos dizer que temos um diretório ~/test com arquivos f1 e f2 :.

mkdir ~/test
cd ~/test
echo "hello" > f1
echo "world" > f2

Agora calculando as somas de verificação com

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum

faz exatamente o que eu quero, lista todos os arquivos do diretório atual e calcula as somas sha1 (a profundidade máxima pode ser alterada posteriormente). A saída no STDOUT é:

f572d396fae9206628714fb2ce00f72e94f2258f  f1
9591818c07e900db7e1e0bc4b884c945e6a61b24  f2

Infelizmente, ao tentar salvá-lo em um arquivo com

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum > sums.sha1

o arquivo resultante exibe a soma de verificação para si mesmo:

da39a3ee5e6b4b0d3255bfef95601890afd80709  sums.sha1
f572d396fae9206628714fb2ce00f72e94f2258f  f1
9591818c07e900db7e1e0bc4b884c945e6a61b24  f2  

e, portanto, falha em um posterior shasum --check , devido ao problema óbvio de modificação de arquivo adicional ao salvar a última soma.

Eu olhei em volta e usando -p flag para xargs , descobri que de alguma forma cria o arquivo de saída antes mesmo de executar o comando find, portanto o arquivo adicional é encontrado e será verificado ...

Eu sei que, como uma solução alternativa, eu poderia salvar a soma de verificação em outro local (diretório temporário via mktemp ) ou excluí-la especificamente, mas gostaria de entender por que ela se comporta da maneira como funciona. meus olhos não são tão úteis, por exemplo, se o primeiro comando verificar se o arquivo de saída já está no disco, ele nunca obterá a resposta correta ...

    
por user121391 29.06.2015 / 14:21

5 respostas

12

Você pode impedir que o arquivo atinja xargs usando:

find . -maxdepth 1 -type f ! -name sums.sha1 -printf '%P\n' |
  xargs -r shasum -- > sums.sha1

Para evitar problemas com o nome do arquivo que têm espaços em branco ou novas linhas ou citações ou barras invertidas, eu usaria:

find . -maxdepth 1 -type f ! -name sums.sha1 -printf '%P
find . -maxdepth 1 -type f ! -name sums.sha1 -printf '%P\n' |
  xargs -r shasum -- > sums.sha1
' | xargs -r0 shasum -- > sums.sha1

em vez disso.

O -- é para evitar problemas com nomes de arquivos que começam com - . No entanto, isso não ajudará em um arquivo chamado - . Se você tivesse usado -print0 em vez de -printf '%P--' , não precisaria do - e não teria um problema com o arquivo %code% .

    
por 29.06.2015 / 14:36
7

Como você está usando -maxdepth 1 , presumo que não queira recursão. Se assim for, basta fazê-lo no shell:

for f in ~/test/*; do
    shasum -- "$f"
done > sums.sha1

Para pular diretórios, você pode fazer:

for f in ~/test/*; do
    [ ! -d "$f" ] && shasum -- "$f"
done > sums.sha1

Se você precisar de recursão e estiver usando bash , faça:

shopt -s globstar
for f in ~/test/**; do
    [ ! -d "$f" ] && shasum -- "$f"
done > sums.sha1

Observe que todas essas abordagens têm o benefício de trabalhar com nomes de arquivos arbitrários, incluindo aqueles com espaços, novas linhas ou qualquer outra coisa.

    
por 29.06.2015 / 14:38
4

com zsh :

shasum -- *(D.) > sums.sha1

O glob será expandido antes do redirecionamento ser feito, portanto, o sums.sha1 não será incluído se não estiver lá.

D é incluir arquivos de pontos (arquivos ocultos) como find . . é selecionar apenas arquivos regulares (como -type f ).

Para excluir o sums.sha1 de qualquer maneira, caso estivesse lá, em primeiro lugar:

setopt extendedglob # best in ~/.zshrc
shasum -- ^sums.sha1(D.) > sums.sha1

Note que aqueles executados um shasum comando, assim você pode acabar vendo um erro "Arg lista muito longa" se a lista é enorme. Para contornar isso:

autoload zargs
zargs -e/ -- *(D.) / shasum > sums.sha1

Eu recomendaria usar ./* em vez de * para evitar possíveis problemas com um arquivo chamado - .

    
por 29.06.2015 / 15:09
1

Como as outras respostas já foram apresentadas, o problema é que o shell é aberto e cria o arquivo sums.sha1 antes de executar o pipeline. Você pode usar o programa sponge que faz parte do pacote moreutils de muitas distribuições. Em contraste com o redirecionamento de shell sponge esperará até receber tudo, antes de abrir o arquivo. É geralmente usado quando você quer escrever um arquivo que você lê no mesmo pipeline.

No seu caso, é usado assim:

$ find -maxdepth 1 -type f -printf '%P\n' |xargs shasum |sponge sums.sha1
$ cat sums.sha1
31836aeaab22dc49555a97edb4c753881432e01d  B
7d157d7c000ae27db146575c08ce30df893d3a64  A
    
por 30.06.2015 / 22:20
0

Como alternativa ao find / xargs etc, você pode querer sha1deep. Provavelmente está em um pacote diferente - na minha caixa vem no pacote md5deep.

Como outros já disseram, o sums.sha1 é criado pelo shell mesmo antes do início da busca. Um truque com ! -name sums.sha1 to find funcionará, assim como

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum | grep -v ' sums\.sha1$' > sums.sha1
    
por 30.06.2015 / 22:29