Meu problema (em um script com #!/bin/sh
) é o seguinte: Tento somar 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 apenas todos os arquivos do diretório atual e calcula as somas sha1 (a profundidade máxima pode ser alterada posteriormente). A saída em STDOUT é:
f572d396fae9206628714fb2ce00f72e94f2258f f1
9591818c07e900db7e1e0bc4b884c945e6a61b24 f2
Infelizmente, ao tentar salvar isso 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:
da39a3ee5e6b4b0d3255bfef95601890afd80709 sums.sha1
f572d396fae9206628714fb2ce00f72e94f2258f f1
9591818c07e900db7e1e0bc4b884c945e6a61b24 f2
e, portanto, falha posteriormente shasum --check
, devido ao problema óbvio de modificação de arquivo adicional ao salvar a última soma.
Olhei em volta e, usando o -p
sinalizador 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á soma de verificação ...
Sei que, como solução alternativa, eu poderia salvar a soma de verificação em outro local (diretório temporário via mktemp
) ou excluí-la na localização específica, mas gostaria de entender por que ela se comporta da maneira que faz - o que, a meu ver, não é tão útil, por exemplo, se o primeiro comando verificar se o arquivo de saída já está no disco, ele nunca obterá a resposta correta ...
xargs
, é o próprio shell que cria esse arquivo, porque antes que qualquer comando seja executado, o shell redireciona todas as entradas, saídas e pipes, de modo que, quandofind
iniciado, o arquivo de saída já existe. Em-exec
vez disso, use :find -maxdepth 1 -type f -exec sh -c 'shasum "$@" > sums.sha1' {} +
sh
chamadas forem necessárias. Observe que você precisa de um argumento para$0
antes{}
.tee
desapareceu? Eu tentei e funciona bem, eu também suprimi STDOUT com adição de1>/dev/null
. Havia algo errado com a resposta ou foi um bug?Respostas:
Você pode impedir que o arquivo chegue
xargs
usando:Para evitar problemas com o nome do arquivo com espaços em branco ou novas linhas ou aspas ou barras invertidas, eu usaria:
em vez de.
O
--
objetivo é evitar problemas com nomes de arquivos que começam com-
. No entanto, não ajudará em um arquivo chamado-
. Se você tivesse usado em-print0
vez de-printf '%P\0'
, não precisaria--
e não teria um problema com o-
arquivo.fonte
basename
obter o nome do arquivo sums.sha1 do caminho completo fornecido (isso não foi incluído na pergunta, mas pode ajudar outras pessoas).Desde que você está usando
-maxdepth 1
, presumo que você não deseja recursão. Nesse caso, basta fazê-lo no shell:Para pular diretórios, você pode:
Se você precisar de recursão e estiver usando
bash
, faça:Observe que todas essas abordagens têm o benefício de trabalhar em nomes de arquivos arbitrários, incluindo aqueles com espaços, novas linhas ou qualquer outra coisa.
fonte
sums.sha1
já estiver lá (de uma execução anterior), sua solução a incorporará.sh
, mas sua resposta pode ajudar outras pessoas.com
zsh
:O globo será expandido antes do redirecionamento, para que
sums.sha1
ele não seja incluído se não estiver lá.D
é incluir arquivos de ponto (arquivos ocultos) comofind
faria..
é selecionar apenas arquivos regulares (como o seu-type f
).Para excluir o
sums.sha1
mesmo assim, caso ele estivesse lá em primeiro lugar:Observe que eles executam um comando shasum; portanto, você pode acabar vendo um erro "Arg list too long" se a lista for enorme. Para contornar isso:
Eu recomendaria usar em
./*
vez de*
evitar possíveis problemas com um arquivo chamado-
.fonte
Como as outras respostas já declararam, o problema é que o shell abre e cria o
sums.sha1
arquivo antes de executar seu pipeline. Você pode usar o programasponge
que faz parte domoreutils
pacote de muitas distribuições. Em contraste com o redirecionamento do shellsponge
, aguarde até receber tudo, antes de abrir o arquivo. Geralmente é usado quando você deseja gravar um arquivo que lê no mesmo pipeline.No seu caso, é usado assim:
fonte
Como alternativa ao find / xargs etc, você pode querer o sha1deep. Provavelmente está em um pacote diferente - na minha caixa, ele vem no pacote md5deep.
Como já foi dito, o sums.sha1 é criado pelo shell mesmo antes do início da localização. Um truque com
! -name sums.sha1
afind
vai funcionar, como a vontadefonte