Como faço para capturar os erros de código de saída / identificador corretamente ao usar a substituição de processo?

13

Eu tenho um script que analisa os nomes dos arquivos em uma matriz usando o método a seguir, extraído de um Q & A sobre SO :

unset ARGS
ARGID="1"
while IFS= read -r -d $'
$ findscript.sh existingfolder nonexistingfolder
find: 'nonexistingfile': No such file or directory
...
' FILE; do ARGS[ARGID++]="$FILE" done < <(find "$@" -type f -name '*.txt' -print0)

Isso funciona muito bem e lida com todos os tipos de variações de nome de arquivo perfeitamente. Às vezes, no entanto, transmitirei um arquivo não existente ao script, por exemplo:

unset ARGS
ARGID="1"
while IFS= read -r -d $'
$ findscript.sh existingfolder nonexistingfolder
find: 'nonexistingfile': No such file or directory
...
' FILE; do ARGS[ARGID++]="$FILE" done < <(find "$@" -type f -name '*.txt' -print0)

Em circunstâncias normais, eu gostaria que o script capturasse o código de saída com algo como RET=$? e o usasse para decidir como proceder. Isso não parece funcionar com a substituição do processo acima.

Qual é o procedimento correto em casos como este? Como posso capturar o código de retorno? Existem outras maneiras mais adequadas para determinar se algo deu errado no processo substituído?

    
por Glutanimate 09.05.2014 / 01:36

4 respostas

4

Você pode obter facilmente o retorno de qualquer processo subescavado, ecoando seu retorno sobre sua stdout. O mesmo acontece com a substituição do processo:

while IFS= read -r -d $'
echo "$return"
' FILE || ! return=$FILE do ARGS[ARGID++]="$FILE" done < <(find . -type f -print0; printf "$?")

Se eu executar isso, a última linha - (ou a seção find delimitada conforme o caso) será o status de retorno de read . $return retornará 1 quando receber um EOF - assim, a única vez em que $FILE é definido como printf é para o último bit de informação lido.

Eu uso \n para não adicionar um extra read ewline - isso é importante porque até mesmo um \n executado regularmente - um no qual você não delimita \n NULs - retornará diferente de 0 nos casos em que os dados que acabou de ler não terminam em $FILE ewline. Portanto, se sua última linha não terminar com || ! return=... ewline, o último valor em sua variável de leitura será seu retorno.

Comando de execução acima e depois:

0

OUTPUT

...
done < <(! find . -type f -print0; printf "$?")
echo "$return"

E se eu alterar a parte de substituição do processo ...

1

OUTPUT

printf \n%s list of lines printed to pipe |
while read v || ! echo "$v"
do :; done

Uma demonstração mais simples:

pipe

OUTPUT

while IFS= read -r -d $'
echo "$return"
' FILE || ! return=$FILE do ARGS[ARGID++]="$FILE" done < <(find . -type f -print0; printf "$?")

E, na verdade, desde que o retorno desejado seja a última coisa que você escreve para stdout dentro da substituição do processo - ou qualquer processo subescavado do qual você lê dessa maneira - então %code% sempre será o status de retorno que você quer quando terminar. E assim, a parte %code% não é estritamente necessária - é usada para demonstrar apenas o conceito.

    
por 30.12.2014 / 23:08
5

Processos na substituição de processos são assíncronos: o shell os lança e, em seguida, não fornece nenhuma maneira de detectar quando eles morrem. Então você não poderá obter o status de saída.

Você pode escrever o status de saída para um arquivo, mas isso é desajeitado em geral, porque você não pode saber quando o arquivo é gravado. Aqui, o arquivo é escrito logo após o final do loop, então é razoável esperar por ele.

… < <(find …; echo $? >find.status.tmp; mv find.status.tmp find.status)
while ! [ -e find.status ]; do sleep 1; done
find_status=$(cat find.status; rm find.status)

Outra abordagem é usar um pipe nomeado e um processo em segundo plano (para o qual você pode usar wait ).

mkfifo find_pipe
find … >find_pipe &
find_pid=$!
… <find_pipe
wait $find_pid
find_status=$?

Se nenhuma das abordagens for adequada, acho que você precisará ir para uma linguagem mais capaz, como Perl, Python ou Ruby.

    
por 09.05.2014 / 03:00
1

Uma abordagem é:

status=0
token="WzNZY3CjqF3qkasn"    # some random string
while read line; do
    if [[ "$line" =~ $token:([[:digit:]]+) ]]; then
        status="${BASH_REMATCH[1]}"
    else
        echo "$line"
    fi
done < <(command; echo "$token:$?")
echo "Return code: $status"

A idéia é fazer eco do status de saída junto com o token aleatório após a conclusão do comando e, em seguida, usar expressões regulares bash para procurar e extrair o status de saída. O token é usado para criar uma string exclusiva para procurar na saída.

Provavelmente não é a melhor maneira de fazer isso em um sentido geral de programação, mas pode ser a maneira menos dolorosa de lidar com isso no bash.

    
por 30.12.2014 / 22:06
1

Use um coprocess . Usando o coproc builtin, você pode iniciar um subprocesso, ler sua saída e verificar seu status de saída:

coproc LS { ls existingdir; }
LS_PID_=$LS_PID
while IFS= read i; do echo "$i"; done <&"$LS"
wait "$LS_PID_"; echo $?

Se o diretório não existir, wait sairá com um código de status diferente de zero.

Atualmente é necessário copiar o PID para outra variável porque $LS_PID será desfeito antes que wait seja chamado. Consulte Bash não definido * _PID variável antes que eu possa esperar na coproc para detalhes.

    
por 17.01.2017 / 12:44