Prática recomendada para usar $? em bash?

9

Quando leio esta resposta sobre $? outra pergunta vem à mente.

Existe alguma prática recomendada de como usar $? em bash?

Vamos dar um exemplo:

Nós temos um script linear e eu gostaríamos de saber que todo o comando foi executado ok. Você acha que está certo chamar uma pequena função (vamos chamá-la de "did_it_work"), para verificar o código de erro e quebrar se não for.

#!/bin/bash 

function did_it_work {
    code=$1
    if [ "$code" -ne "0" ]
    then
        echo "Error failure: code $code "
        exit 1
    fi
}

dir=some/path

mkdir -p $dir
did_it_work $? 

cd $dir
did_it_work $? 

run_some_command
did_it_work $? 

Essa abordagem, claro, significa que eu tenho que resolver manualmente o problema se houver algum e reexecutar o script.

Você acha que essa é uma boa ideia ou há alguma outra prática recomendada para fazer isso?

/ Obrigado

    
por Johan 07.03.2011 / 17:35

3 respostas

13

Uma maneira comum é:

die() {
    echo "$*" 1>&2
    exit 1
}

então você usa assim:

mkdir -p some/path || die "mkdir failed with status $?"

Ou, se você quiser incluir o status de saída, altere-o para:

die() {
    echo "FATAL ERROR: $* (status $?)" 1>&2
    exit 1
}

e, em seguida, usá-lo é um pouco mais fácil:

mkdir -p some/path || die "mkdir failed"

Caso você não tenha visto command1 || command2 antes, ele executa command1 e, se command1 falhar, será executado command2 .

Você pode ler como "crie o diretório ou morra".

Seu exemplo ficaria assim:

mkdir -p some/path || die "mkdir failed"
cd some/path || die "cd failed"
run_some_command || die "some_command failed"

Ou você pode alinhar o dies mais à direita para que o código principal seja mais óbvio.

mkdir -p some/path         || die "mkdir failed"
cd some/path               || die "cd failed"
run_some_command           || die "some_command failed"

Além disso, se você for usar o nome some/path várias vezes, armazene-o em uma variável para não precisar continuar digitando e, se necessário, altere-o facilmente.

dir=some/path
mkdir -p "$dir"            || die "Cannot make $dir"
cd "$dir"                  || die "Cannot cd to $dir"
run_some_command           || die "Cannot run some_command"

E se você planeja corrigir o problema e executar novamente o script, talvez queira que o script funcione se o diretório já existir, portanto, não é necessário removê-lo primeiro. Nesse caso, você deseja

dir=some/path
if [ ! -d "$dir" ]; then
    mkdir -p "$dir"        || die "Cannot make $dir"
fi
cd "$dir"                  || die "Cannot cd to $dir"
run_some_command           || die "Cannot run some_command"
    
por 07.03.2011 / 20:54
8

Você pode reescrever seu código assim:

#!/bin/bash
function try {
    "$@"
    code=$?
    if [ $code -ne 0 ]
    then
        echo "$1 did not work: exit status $code"
        exit 1
    fi
}

try mkdir -p some/path
try cd some/path
try run_some_command

Se você realmente não precisa registrar o código de erro, mas apenas se o comando foi bem-sucedido ou não, é possível reduzir ainda mais try() da seguinte forma:

function try {
    if ! "$@"
    then
        echo "$1 did not work"
        exit 1
    fi
}
    
por 07.03.2011 / 17:47
6

Se você realmente quiser exit em um erro e estiver usando o Bash, considere também set -e . De help set :

-e Exit immediately if a command exits with a non-zero status.

Isso, é claro, não oferece a flexibilidade de uma função did_it_work (), mas é uma maneira fácil de garantir que o script bash pare em um erro sem adicionar muitas chamadas à nova função.

    
por 07.03.2011 / 19:14