Записать код завершения команды, аналогично команде времени

10

с помощью

time sleep 1

выходы:

$ time sleep 1

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

Есть ли команда, которую я могу использовать, чтобы напечатать код выхода sleepили любую команду, которую я хочу выполнить?

Что-то нравится:

$ log-exit-code sleep 1

возможно этого достаточно?

sleep 1 && echo "$?"
Александр Миллс
источник
3
Нет, sleep 1 && echo $?напечатал бы код спящей ячейки, только когда она равна нулю ...
Satō
о, лол, ты прав
Александр Миллс
1
sleep 1 && echo "$?" || echo "$?"
jesse_b
2
Я добавил ответ, так как все остальные до сих пор отбрасывают исходное возвращаемое значение, что, по моему мнению, плохо (после этих ответов $? Всегда равен 0, так как последнее, что они делают, это disply, который всегда работает. Они все забыли сохранить исходное возвращаемое значение и возвращают его после отображения. Поэтому они просто показывают его наблюдателю, но «скрывают» его для остальной части сценария, который теперь не может использовать «$?», чтобы увидеть, возвращает ли эта важная команда 0 или что-то еще ...)
Оливье Дюлак
1
Также все решения ниже могут легко реализовать эту функциональность. Кусалананда, например, будет просто:EXIT_CODE=$(tellexit command)
jesse_b

Ответы:

4

В моем тестировании до сих пор это работало:

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

Просто говорит ему, чтобы повторить код выхода, если он успешен или если он терпит неудачу.

Как Сато указал ниже, это по сути то же самое, что и:

command; echo "$?"

Одна вещь, которая может сделать команду and / or стоящей, это что-то вроде:

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

Если вам нужен ваш сценарий для обработки кода выхода, как забота Оливье, это не проблема. Ваш скрипт может выглядеть примерно так:

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
источник
13
Э-э, как это лучше, чем просто command; echo $??
Satō
4
Я думаю, это не так. command; echo $?это гораздо лучший ответ.
jesse_b
э-э-э, похоже, это лучший вариант, поэтому я выбрал его в качестве ответа, если кто-то не согласен, lmk
Александр Миллс,
@AlexanderMills Только вы знаете, что является наиболее подходящим ответом на ваш вопрос :-)
Кусалананда
1
Мне не нравится это: это меняет код завершения после, command ; echo $?потому что echo $?всегда успешно, и, следовательно, после него, новое значение $? теперь равно 0. Существует способ сохранить исходный код возврата даже после его отображения, в случае, если этот исходный код возврата будет полезен позже (например, в скрипте проверка кода возврата важна до принятия решения о том, как продолжить). Смотрите мой ответ для одного из нескольких решений (большинство ответов здесь можно изменить таким же образом)
Оливье Дюлак
11

cmd && echo "$?"не будет работать, так как по необходимости будет только печатать нули ( echoбудет выполняться только при успешном завершении предыдущей команды).

Вот короткая функция оболочки для вас:

tellexit () {
    "$@"

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

Это печатает код завершения данной команды так же, как и timeкоманда.

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

$ tellexit false
exit code       1

Перенаправляя printfto /dev/ttyв функцию, мы все еще можем использовать tellexitс перенаправлениями, не получая мусор в наших стандартных выходных данных или потоках ошибок:

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

Сохраняя код выхода в переменной, мы можем вернуть его вызывающей стороне:

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

Более классная версия той же функции также печатает сигнал, который убил команду, если код выхода больше 128 (что означает, что она прервана из-за сигнала):

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"
}

Тестирование:

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

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

(Для localэтого требуется ash/ pdksh/ bash/ zsh, или вы можете изменить его на то, typesetчто понимают несколько других оболочек.)

Кусалананда
источник
круто, как бы вы использовали tellexit, покажите его в действии, пожалуйста
Александр Миллс
1
@AlexanderMills Смотрите обновление.
Кусалананда
2
Он должен сохранить исходное возвращаемое значение и вернуть его в конце.
Оливье Дюлак
@OlivierDulac Действительно хорошее наблюдение! Я исправлю.
Кусалананда
7

Используйте функцию оболочки оболочки вещь. Вероятно, с другим именем.

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

(Это, конечно, приведет к повреждению стандартного вывода, поэтому либо используйте параметр, ttyпоказанный @Kusalananda, либо не используйте его вне интерактивного контекста.)

Переходя на непортативную территорию, некоторые оболочки могут сообщать о состоянии всех команд в конвейере, а не только о последней, например, в ZSH, если вы хотите, чтобы сбои сообщались из всего конвейера:

% 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 в противном случае не срабатывает при отсутствии ошибки (по принципу «нет новостей - хорошие новости»).

thrig
источник
1
Он должен сохранить исходное возвращаемое значение и вернуть его в конце. Отображение (printf, echo) простой вещи (не начинается с '-' и т. Д.) Всегда работает, и после этого отображается "$?" теперь имеет значение «0», как дисплей работал
Оливье Дюлак
7

У GNU timeесть опция для этого:

time -f %x sleep 1
0

Проходит через код выхода 1 , если не убит сигналом 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

Если вы хотите узнать / обработать ситуацию уничтожения сигнала, передайте -vи наберите stderr для строки Command terminated by signal.


1 Спасибо Оливье Дюлаку за то, что он отметил, что код выхода проходит.
2 Также, спасибо Стефану Шазеласу за то, что он указал, что код выхода сигнала уничтожения не проходит.

епископ
источник
1
Interresting. Только GNU, но интересно, так как этот (вероятно) сохранит исходный код возврата для последующего кода (т. Е. Он не заменяет его на «0», как это делают другие ответы)
Оливье Дюлак
2
Это возвращает, 0хотя, если команда убита.
Стефан
2
@bishop, я согласен, что 0 в этих случаях - меньше, чем идеал. Но обратите внимание, что нет общего согласия относительно того, что сообщать, когда команда убита сигналом. Возвращение 128 + номер сигнала используется несколькими оболочками. Вы также видите такие вещи, как 256+signumили, 384+signumили даже, signum/256или signum/256+0.5если ядро ​​было сгенерировано (фактически статус, возвращаемый waitpid()делением на 256). Некоторые дали бы текстовое представление (как sigintили sigquit+core). Все же, вероятно, стоит обсудить в группе новостей gnu.coreutils.bugs, я согласен.
Стефан
3
(на самом деле timeэто не часть coreutils, это пакет сам по себе с собственным списком рассылки об ошибках (bug-time@gnu.org))
Стефан
1
@bishop, system()из традиционных awk, подобных тем, что до сих пор поддерживают Брайан Керниган ( kin awk) или nawkиз Solaris, awkFreeBSD, которые вытекают из этого. awk 'BEGIN{print system("kill -s ABRT $$")}'выводится, 0.523438когда размер дампа ядра не ограничен. Поведение gawkизменилось недавно, с изменяющимся поведением с --traditionalили --posix(см. Также возвращаемое значение close(cmd)для большего разнообразия)
Стефан
2

Мне не нравятся все остальные ответы (хотя мне очень нравится их ум): они отображают код выхода, но они также меняют его. (отображаемая часть истинна, следовательно, после нее код возврата равен 0)

Вот модифицированная версия:

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
Оливье Дюлак
источник
на самом деле, @bishop answer - единственный ответ, который сохраняет это значение, но полагается на версию GNU, timeкоторая не всегда доступна.
Оливье Дюлак
2

Чтобы быть немного более устойчивым, отправьте статус выхода на отдельный FD, чтобы он мог обрабатываться независимо от 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

Обратите внимание, что вам нужно что-то сделать с FD3, или он скажет Bad file descriptor.

Это может быть дополнительно сконфигурировано для предоставления этих выходов на входы другой программы, когда другая программа прослушивает более одного FD; Вы могли бы использовать это как своего рода Кибана начала 90-х :)

onwsk8r
источник
Он должен сохранить исходное возвращаемое значение и вернуть его в конце. Отображение (printf, echo) простой вещи (не начинается с '-' и т. Д.) Всегда работает, и после этого отображается "$?" теперь имеет значение «0», как дисплей работал
Оливье Дюлак
1

В то время как все другие (очень интересные) ответы касаются точного вопроса, который задал ОП, на практике все обычно проще. Вы просто сохраняете статус выхода в переменную (сразу после команды) и сообщаете или регистрируете ее любым способом, каким хотите. Например, распечатать в / dev / stderr или написать / добавить его в виде текстового сообщения в файл журнала. И это также сохраняется для использования при принятии решений / управлении потоком в последующем коде.

Если это в функции:

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
}

Если это прямо в скрипте:

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

(Уклонение и покрытие, потому что это не отвечает на точный вопрос.)

Джо
источник