Código de comando de saída de log, semelhante ao comando time

10

usando

time sleep 1

rendimentos:

$ time sleep 1

real    0m1.005s
user    0m0.001s
sys     0m0.001s

existe um comando que eu possa usar para imprimir o código de saída sleepou qualquer comando que eu queira executar?

Algo gosta:

$ log-exit-code sleep 1

talvez isso seja suficiente?

sleep 1 && echo "$?"
Alexander Mills
fonte
3
Não, sleep 1 && echo $?iria imprimir o código da célula dormente apenas quando é de zero ...
Satō Katsura
oh lol você está certo #
Alexander Mills
1
sleep 1 && echo "$?" || echo "$?"
jesse_b
2
Eu adicionei uma resposta, já que todas as outras até agora descartam o valor de retorno original, o que é ruim (depois dessas respostas, $? É sempre 0, pois a última coisa que eles fazem é um disply, que sempre funciona. Todos se esqueceram de manter o valor de retorno original e retorne após exibi-lo.Portanto, eles apenas o mostram a um observador, mas o ocultam pelo restante do script, que não pode usar agora "$?" para ver se esse comando importante retorna 0 ou algo mais ...)
Olivier Dulac 08/09
1
Além disso, todas as soluções abaixo podem implementar facilmente essa funcionalidade. O de Kusalananda, por exemplo, seria apenas:EXIT_CODE=$(tellexit command)
jesse_b 08/09

Respostas:

4

Nos meus testes até agora, isso funcionou:

command && echo "$?" || echo "$?"

Apenas diz a ele para ecoar o código de saída, se for bem-sucedido ou se falhar.

Como Sato apontou abaixo, é essencialmente o mesmo que:

command; echo "$?"

Uma coisa que pode fazer com que o comando e / ou valha a pena é algo como:

command && echo "Success! Exit Code: $?" || echo "Failure! Exit Code: $?"

Se você precisa que seu script atue no código de saída, como a preocupação de Olivier, isso não é problema. Seu script pode ser algo como:

command
case "$?" in; 
    0) echo "Command exited with: 0"
       <command if good>
       ;;
    1) echo "Command exited with: 1"
        <command if bad>
        ;;
    255) echo "Command exited with: 255"  # for ssh maybe?
         <command if 255>
         ;;
    *) echo "Command exited with: >2"
        <command for other exit code>
        ;;
esac
jesse_b
fonte
13
Err, como isso é melhor do que justo command; echo $??
Satō Katsura
4
Eu acho que não é. command; echo $?é uma resposta muito melhor.
jesse_b
uhhh isso parece que é a melhor opção, então eu escolhi-o como a resposta, se alguém discorda, lmk
Alexander Mills
@AlexanderMills Somente você sabe qual é a resposta mais adequada à sua pergunta :-)
Kusalananda
1
Não gosto disso: isso altera o código de saída depois command ; echo $?porque echo $?sempre é bem-sucedido e, portanto, depois dele, o novo valor de $? agora é 0. Existe uma maneira de manter o código de retorno original mesmo depois de exibi-lo, caso esse código de retorno original seja útil mais tarde (por exemplo: em um script, a verificação do código de retorno é importante antes de decidir como continuar). Veja a minha resposta para uma das várias soluções (a maioria das respostas aqui podem ser alteradas da mesma forma)
Olivier Dulac
11

cmd && echo "$?"não funcionaria, uma vez que, necessariamente, imprimiria zeros ( echosomente seria executado com a conclusão bem-sucedida do comando anterior).

Aqui está uma função de shell curta para você:

tellexit () {
    "$@"

    local err="$?"
    printf 'exit code\t%d\n' "$err" >/dev/tty
    return "$err"
}

Isso imprime o código de saída do comando fornecido de maneira semelhante ao timecomando.

$ tellexit echo "hello world"
hello world
exit code       0

$ tellexit false
exit code       1

Ao redirecionar o printfpara /dev/ttyna função, ainda podemos usar tellexitredirecionamentos sem obter lixo em nossos fluxos de saída ou erro padrão:

$ tellexit bash -c 'echo hello; echo world >&2' >out 2>err
exit code       0
$ cat out
hello
$ cat err
world

Ao salvar o código de saída em uma variável, podemos devolvê-lo ao chamador:

$ tellexit false || echo 'failed'
exit code       1
failed

Uma versão mais sofisticada da mesma função também imprime o sinal que interrompeu o comando se o código de saída for maior que 128 (o que significa que ele terminou devido a um sinal):

tellexit () {
    "$@"

    local err="$?"

    if [ "$err" -gt 128 ]; then
        printf 'exit code\t%d (%s)\n' "$err" "$(kill -l "$err")" >/dev/tty
    else
        printf 'exit code\t%d\n' "$err" >/dev/tty
    fi

    return "$err"
}

Teste:

$ tellexit sh -c 'kill $$'
exit code       143 (TERM)

$ tellexit sh -c 'kill -9 $$'
Killed
exit code       137 (KILL)

(A localcoisa requer ash/ pdksh/ bash/ zsh, ou você pode alterá-la para a typesetqual algumas outras conchas também entendem.)

Kusalananda
fonte
legal, como você usaria tellexit, mostre-nos pls ação
Alexander Mills
1
@AlexanderMills Ver atualização.
Kusalananda
2
Ele deve manter o valor de retorno original e devolvê-lo no final.
Olivier Dulac
@OlivierDulac Realmente boa observação! Eu vou alterar.
Kusalananda
7

Use uma função de invólucro de shell. Provavelmente com um nome diferente.

$ exito() { "$@"; echo $?; }
$ exito true
0
$ exito false
1
$ exito echo "test test"      
test test
0
$ 

(Obviamente, isso corromperá a saída padrão; portanto, use o ttymostrado por @Kusalananda ou não fora de contextos interativos.)

Ao sair para um território não transportável, algumas shells podem relatar o status de todos os comandos em um pipeline, não apenas o último, por exemplo, no ZSH, se você deseja que as falhas sejam relatadas em um pipeline inteiro:

% TRAPZERR() { print >/dev/tty $pipestatus }
% perl -e 'exit 42' | perl -e 'exit 99'
42 99
% false | perl -e 'exit 42' | perl -e 'exit 99'
1 42 99
% perl -e 'exit 42' | perl -e 'exit 99' | true
% 

TRAPZERR caso contrário, não será acionado quando não houver erro (no principal "nenhuma notícia é boa notícia").

agitar
fonte
1
Ele deve manter o valor de retorno original e devolvê-lo no final. Exibindo (printf, eco) uma coisa simples (não começa com '-', etc) sempre funciona, e depois disso exibe "$?" agora tem o valor "0" como a exibição trabalhou
Olivier Dulac
7

O GNU timetem uma opção para isso:

time -f %x sleep 1
0

Passa pelo código de saída 1 , a menos que seja morto por um sinal 2 :

$ /usr/bin/time -f %x sleep 1; echo $?
0
0

$ /usr/bin/time -f %x sleep 2x; echo $?
sleep: invalid time interval ‘2x’
Try 'sleep --help' for more information.
1
1

$ /usr/bin/time -f %x sleep 60s; echo $? # Press ^C before a minute elapses
0
2

Se você deseja conhecer / lidar com a situação de interrupção do sinal, passe -ve grep o stderr para a string Command terminated by signal.


1 Obrigado a Olivier Dulac por observar o código de saída.
2 Além disso, obrigado Stéphane Chazelas por apontar o código de saída do sinal de morte não passa.

bispo
fonte
1
Interessante. Apenas GNU, mas interessante, pois esse (provavelmente) manterá o código de retorno original para o restante do código (ou seja, ele não o substituirá por "0" como as outras respostas)
Olivier Dulac
2
Retorna 0embora se o comando for morto.
Stéphane Chazelas
2
@ Bispo, concordo que 0 nesses casos seja inferior ao ideal. Mas observe que, não há acordo geral sobre o que relatar quando um comando é morto por um sinal. O número do sinal 128 + retornado é usado por algumas conchas. Você também vê coisas como 256+signumou 384+signumou mesmo signum/256ou signum/256+0.5se um núcleo foi gerado (na verdade, o status retornado por waitpid()dividido por 256). Alguns dariam uma representação textual (como sigintou sigquit+core). Ainda vale a pena discutir no grupo de notícias gnu.coreutils.bugs, eu concordo.
Stéphane Chazelas
3
(na verdade, timenão faz parte da coreutils, é um pacote por conta própria com a sua lista de discussão própria bug ([email protected]))
Stéphane Chazelas
1
@bishop, o system()dos awks tradicionais como o ainda mantido por Brian Kernighan (o kin awk) ou o nawkdo Solaris, o awkdo FreeBSD que deriva disso. awk 'BEGIN{print system("kill -s ABRT $$")}'saídas 0.523438quando o tamanho do dump principal não é limitado. O comportamento de gawkmudou recentemente, com diferentes comportamento com --traditionalou --posix(ver também o valor de retorno de close(cmd)uma maior variação)
Stéphane Chazelas
2

Não gosto de todas as outras respostas (embora eu goste muito da inteligência): elas exibem o código de saída, mas também o ALTERAM. (a parte exibida é verdadeira, portanto, depois disso, o código de retorno é 0)

Aqui está uma versão modificada:

do_and_tell () {
   "$@"
   returncode="$?"
   printf "Execution of : \n%s\n yelded a return code of: \n%s\n" "$*" "$returncode"
   return $returncode  # the interresting addition... keeps the commands return value.
}

## usage:

prompt$ do_and_tell true
Execution of : 
true
 yelded a return code of: 
0

prompt$ echo $?
0

prompt$ do_and_tell false
Execution of : 
false
 yelded a return code of: 
1

prompt$ echo $?
1
Olivier Dulac
fonte
na verdade, a resposta @bishop é a única que mantém esse valor, mas depende da versão do GNU timenem sempre disponível.
Olivier Dulac
2

Para ser um pouco mais robusto, envie o status de saída para um FD separado, para que ele possa ser tratado independentemente do stdout / stderr:

exit_status() {
    "$@"
    rv=$?
    echo >&3 "$rv"
    return "$rv"
}

# exit status and stdout to stdout:
> exit_status echo "hello" 3>&1
hello
0

# exit_status to stdout, stdout to /dev/null
> exit_status echo "hello" 3>&1 1>/dev/null
0
> exit_status ehco "hello" 3>&1 1>/dev/null
ehco: command not found
127

# exit_status to stdout, stdout and stderr to /dev/null
> exit_status ehco "hello" 3>&1 &>/dev/null
127

Observe que você precisará fazer algo com o FD3 ou ele dirá Bad file descriptor.

Isso pode ser configurado ainda mais para fornecer essas saídas às entradas de outro programa, fazendo com que o outro programa escute em mais de um FD; você pode usar isso como uma espécie de Kibana dos anos 90 :)

onwsk8r
fonte
Ele deve manter o valor de retorno original e devolvê-lo no final. Exibindo (printf, eco) uma coisa simples (não começa com '-', etc) sempre funciona, e depois disso exibe "$?" agora tem o valor "0" como a exibição trabalhou
Olivier Dulac
1

Embora todas as outras respostas (muito interessantes) abordem a pergunta exata que o OP fez, na prática, as coisas geralmente são mais simples. Você acabou de salvar o status de saída em uma variável (imediatamente após o comando) e relatá-lo ou registrá-lo da maneira que desejar. Por exemplo, imprima em / dev / stderr ou escreva / anexe-o como uma mensagem de texto em um arquivo de log. E também é preservado para uso na tomada de decisão / controle de fluxo no código subseqüente.

Se estiver em uma função:

function some-function () {
    local rc ## to avoid side effects
    ...
    some-command ## run any command
    rc=$?  ## save the return status in a variable
    echo "some-command returned $rc" ## optionally tell the user about it
    ...
    return $rc  ## at the end, if appropriate
}

Se estiver diretamente em um script:

some-command ## run any command
rc=$?  ## save the return status in a variable
echo "some-command returned $rc" ## optionally tell the user about it
...
exit $rc    ## at the end to tell any calling script about it, if appropriate

(Abaixando e cobrindo porque isso não responde à pergunta exata .)

Joe
fonte