Журнал виходу коду команди, аналогічний команді time


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ō Katsura

о, хале, ви маєте рацію
Олександр Міллз

1
sleep 1 && echo "$?" || echo "$?"
jesse_b

2
Я додав відповідь, оскільки всі інші поки що відкидають початкове повернене значення, що imo є поганим (після цих відповідей $? Завжди 0, як останнє, що вони роблять, - це disply, який завжди працює. Вони всі забули зберегти початкове значення повернення та повертає його після відображення. Тому вони просто показують його спостерігачеві, але "приховують" його для решти сценарію, який тепер не може використовувати "$?", щоб побачити, чи повертається ця важлива команда 0 чи щось інше ...)
Олів'є Дулак

1
Також всі рішення нижче можуть легко реалізувати цю функціональність. Наприклад, Кусалананди буде:EXIT_CODE=$(tellexit command)
jesse_b

Відповіді:


4

У моєму тестуванні поки що це спрацювало:

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

Просто вказує на те, що він повторює код виходу, якщо він успішний або невдалий.

Як Сато зазначив нижче, це по суті те саме, що:

command; echo "$?"

Одне, що може зробити та / або команду варті, - це щось на зразок:

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

13
Помилка, як це краще, ніж просто command; echo $??
Satō Katsura

4
Я думаю, це не так. command; echo $?набагато краща відповідь.
jesse_b

uhhh, здається, це найкращий варіант, тому я вибрав це як відповідь, якщо хто не погоджується, lmk
Alexander Mills

@AlexanderMills Тільки ви знаєте, яка найбільш відповідна відповідь на ваше запитання :-)
Kusalananda

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

Перенаправивши printfдо /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, покажи це в екшену pls
Олександр Міллз

1
@AlexanderMills Дивіться оновлення.
Кусалаланда

2
Він повинен зберігати початкове повернене значення і повертати його в підсумку.
Олів’є Дулак

@OlivierDulac Дійсно гарне спостереження! Я буду вносити зміни.
Kusalananda

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 інакше не спрацьовує, коли немає помилок (за принципом "немає новин - це хороша новина").


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
Інтересуючий. 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))
Stéphane Chazelas

1
@bishop, system()традиційні дивовижі, схожі на той, який досі підтримує Брайан Керніган ( kв awk) або nawkСоляріс, awkFreeBSD, що випливає з цього. awk 'BEGIN{print system("kill -s ABRT $$")}'Виводи, 0.523438коли розмір дампів ядра не обмежений. Нещодавно поведінка gawkзмінилася, змінюючи поведінку з --traditionalабо --posix(див. Також повернене значення close(cmd)для більшої зміни)
Stéphane Chazelas

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 - це єдиний інший, який зберігає це значення, але покладається на версію 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-х років :)


Він повинен зберігати початкове повернене значення і повертати його в підсумку. Відображення (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

(Качка і покриття, оскільки це не відповідає точному питанню.)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.