Como obter o código de saída dos comandos iniciados por find?

2

Estou usando o "find" em um Travis-CI para verificar um determinado tipo de arquivo com um programa. (Para ser exato, é uma verificação de shellcheck.)

No entanto, ao usar find , os códigos de saída dos comandos / subshells executados por ele são naturalmente descartados, pois não são passados para o "script principal".

Como exemplo, este é um comando de localização:

find . -type f -iname "*.sh" -exec sh ./testScripts.sh "{}" \;

./testScripts.sh pode sair com 0 ou > = 1, dependendo do resultado do teste.

O testScripts.sh sai corretamente com o código de saída correto, mas devido a find , o código de saída do comando é sempre "0". Tudo que eu quero é que, se um arquivo / execução erros, este erro é "propagado" até o Travis-CI.

Como posso conseguir isso?

    
por rugk 18.09.2017 / 16:48

2 respostas

3

Usando a sugestão de Stephen Kitt nos comentários:

find . -type f -iname "*.sh" -exec sh -c 'for n; do ./testScripts.sh "$n" || exit 1; done' sh {} +

Isso fará com que o script sh -c saia com um status de saída diferente de zero, assim como testScript.sh . Isso significa que find também sairá com um status de saída diferente de zero:

If terminated by a plus sign, the pathnames for which the primary is evaluated are aggregated into sets, and utility will be invoked once per set, similar to xargs(1). If any invocation exits with a non-zero exit status, then find will eventually do so as well, but this does not cause find to exit early.

Com relação às perguntas no comentário:

  1. for n; do ... ; done parece estranho, mas faz sentido quando você percebe que sem nada para iterar, o for irá iterar sobre "$@" implicitamente.

  2. O sh final no final será colocado em $0 do shell sh -c . O {} será substituído por um número de caminhos. Sem sh , o primeiro nome de caminho terminaria em $0 e não seria escolhido pelo loop, já que não está em $@ . $0 geralmente contém o nome do intérprete atual (bem, seu valor é arbitrário, algumas pessoas usam _ aqui).

por 18.09.2017 / 17:03
1

xargs sairá com um status de saída entre 1 e 125 (123 com GNU xargs ), se qualquer um dos comandos falhar, e será cancelado se algum falhar com um status 255.

Para usar xargs de forma confiável na saída de find (com -print0 ) e preservar o stdin do comando, você precisará do GNU xargs . Então, com GNU xargs e um shell com suporte para substituição de processo como ksh , zsh ou bash :

xargs -n1 -r0a <(find . -type f -iname '*.sh' -print0) sh ./testScripts.sh

Ou para abortar na primeira falha:

xargs -r0a <(find . -type f -iname '*.sh' -print0) sh -c '
  for file do
    sh ./testScripts.sh "$file" || exit 255
  done' sh

Você também pode abortar find no primeiro erro com (código POSIX):

find . -type f -name '*.[sS][hH]' -exec sh -c '
  for file do
    if ! sh ./testScripts.sh "$file"; then
      kill -s PIPE "$PPID"
      exit 1
    fi
  done' sh {} +

(usando SIGPIPE como um sinal menos barulhento com alguns shells como bash ). Isso fará com que find seja eliminado e retorne com um status de saída diferente de zero.

Para obter o valor exato do status de saída do (neste último caso) pedido com falha, com zsh ou bash , você também pode fazer:

ret=0
while IFS= read -rd '' -u3 file; do
  sh ./testScripts.sh "$file" 3<&- || ret=$?
done 3< <(find . -type f -iname '*.sh' -print0)

Embora com zsh , você nem precisa de find para isso:

set -o extendedglob
ret=0
for file (./**/*(#i).sh(D.)) {
  ./testScripts.sh $file || ret=$?
}
    
por 18.09.2017 / 18:33