Найкраща практика використання $? в баш?


10

Коли я прочитав цю відповідь про $? інше питання приходить на думку.

Чи є найкраща практика використання $? в баш?


Наведемо приклад:

У нас лінійний сценарій, і я хотів би знати, що всі команди виконані нормально. Чи вважаєте ви, що це нормально, щоб викликати невелику функцію (назвемо її "did_it_work"), перевірити код помилки та зламати її, якщо її немає.

#!/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 $? 

Такий підхід, звичайно, означає, що мені доведеться вручну вирішити проблему, якщо вона є, і повторити сценарій.

Ви вважаєте, що це гарна ідея чи є якась інша найкраща практика для цього?

/Дякую

Відповіді:


15

Один поширений спосіб:

die() {
    IFS=' ' # make sure "$*" is joined with spaces

    # output the arguments if any on stderr:
    [ "$#" -eq 0 ] || printf '%s\n' "$*" 1>&2
    exit 1
}

то ви використовуєте його так:

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

Або якщо ви хочете, щоб він включав статус виходу, ви можете змінити його на:

die() {
    last_exit_status=$?
    IFS=' '
    printf '%s\n' "FATAL ERROR: $* (status $last_exit_status)" 1>&2
    exit 1
}

а потім використовувати його трохи простіше:

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

Якщо це не вдасться, mkdirймовірно, вже випустили повідомлення про помилку, так що друге може сприйматись як зайве, і ви можете просто зробити:

mkdir -p some/path || exit   # with the same (failing) exit status as mkdir's
mkdir -p some/path || exit 1 # with exit status 1 always

(або використовувати перший варіант dieвище без аргументів)

Про всяк випадок, коли ви його раніше не бачили command1 || command2, він працює command1, а якщо command1виходить з ладу - він працює command2.

Таким чином, ви можете прочитати це як "зробити каталог або померти".

Ваш приклад виглядатиме так:

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

Або ви можете вирівняти diesдалі праворуч, щоб основний код був більш очевидним.

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

Або в наступному рядку, коли командні рядки довгі:

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

cd some/path ||
  die "cd failed"

some_command ||
  die "some_command failed"

Крім того, якщо ви збираєтесь використовувати ім'я some/pathкілька разів, зберігайте його в змінній, щоб вам не довелося продовжувати вводити його, і ви можете легко змінити його, якщо потрібно. І при передачі змінних аргументів командам, не забудьте використовувати --роздільник параметрів, щоб аргумент не приймався як варіант, якщо він починається з -.

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

9

Ви можете переписати свій код так:

#!/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

Якщо насправді вам не потрібно вводити код помилки, а лише вдалося чи ні команда, можна скоротити try()далі так:

function try {
    if ! "$@"
    then
        echo "$1 did not work"
        exit 1
    fi
}

Ви також можете використовувати код у такому форматі. <pre> функція спробуйте {
BillThor

Якщо ви використовуєте цю функціональність в лінію і не хочете , щоб повернутися з істинної боку , ви можете замінити return $?з :вбудованою командою.
BillThor

8

Якщо ви дійсно хочете exitпомилитися і використовуєте Bash, то вам також слід розглянути set -e. Від help set:

-e Вийдіть негайно, якщо команда завершиться з ненульовим статусом.

Це, звичайно, не дає вам гнучкості функції did_it_work (), але це простий спосіб переконатися, що ваш скрипт bash зупинився на помилці, не додаючи багато дзвінків до вашої нової функції.


set -eкорисно. Є деякі команди, які повертають ненульові значення за звичайних обставин (наприклад, diff). Коли я використовую set -e в сценарії, де я очікую повернення нуля, я це роблю command || true.
Шон Дж. Гофф

2
Крім того, навіть якщо ви використовуєте set -e, ви можете встановити "обробник винятків", щоб вирішити всі помилки trap did_it_work EXIT.
Жил "ТАК - перестань бути злим"

1
Це частина POSIX, а не специфічна особливість. Використовуйте це, але будьте в курсі деяких підводних каменів mywiki.wooledge.org/BashFAQ/105
kmkaplan

@ ShawnJ.Goff Я вважаю за краще робити command && true. Як і те, що значення повернення не змінюється.
kmkaplan

@kmkaplan Ваш останній коментар не має для мене сенсу. Мета command || true- запобігти set -eвиходу зі скрипту, якщо commandповерне ненульовий код виходу. Він змінює вихідний код, оскільки нам це потрібно. Єдине, command && trueщо потрібно запустити true(повернути нульовий код виходу), якщо команда `вдалася (повернула нульовий код виходу) - це повна неоперація.
трійка
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.