Використовуючи $? у заяві if


12
function foo {
   (cd $FOOBAR;
   <some command>
   if [$? -ne 0]
   then
      echo "Nope!"
   else
      echo "OK!"
   fi
   )
}

Я намагаюся написати таку функцію, як описана вище, і помістити її у мій файл .bashrc. Після джерела файлу та запуску я отримую:

Загальний час: 51 секунда
-bash: [1: команда не знайдена
ОК!

Чи може хтось допомогти мені зрозуміти, що я зробив не так?


4
Тестування, якщо $?дорівнює 0, з ifтвердженням є безглуздим, ifочікує на команду, і якщо згадана команда повернеться 0, вона запускає код у блоці. так if true; then echo hello; fiбуде лунати привіт з моменту trueповернення команди 0.
llua

1
@llua Це не безглуздо. $?містить статус останнього конвеєра , який не є командою test( [) у ifвиписці. Приклад - тестування того, чи some commandбуло успішно. Можна зробити те ж саме з &&і ||, але це може робити довгі, нечитабельні лінії порівняно з if [ $? -eq 0 ]. Той самий аргумент стосуєтьсяif some command
bonsaiviking

@bonsaiviking Я добре знаю, до чого $?розширюється, я вказую , що немає сенсу testвикористовуватись; оскільки if some commandце те ж саме відбувається з одним твердженням проти двох окремих тверджень. якщо команда вже довга, додавання ще трьох символів не зробить "нечитабельний" жахливішим "нечитабельним".
llua

Відповіді:


33

Додайте пробіл після [та ще один перед ]:

function foo {
   (cd $FOOBAR;
   <some command>
   if [ $? -ne 0 ]
   then
      echo "Nope!"
   else
      echo "OK!"
   fi
   )
}

[це вбудована команда оболонки, це команда так само , як echo, read, expr... він потребує просторі після неї, і вимагає узгодження ].

Запис [ $? -ne 0 ]насправді виклик [і надання їй 4 параметра: $?, -ne, 0, і ].

Примітка: той факт, що ви отримуєте повідомлення про помилку, [1: command not foundозначає, що $?має значення 1.


1
Я щойно переконався, що ваша відповідь правильна у моїй Linux VM.
саміам

[це посилання , testа також
Ріккі Beam

2
@ RickyBeam в більшості оболонок [є вбудованою оболонкою, і /usr/bin/[використовується рідко.
Патрік

20

Або ви могли пропустити $?зовсім. Якщо ваша команда cmd, слід працювати наступним чином:

function foo {
   (cd $FOOBAR;
   if cmd
   then
      echo "OK!"
   else
      echo "Nope!"
   fi
   )
}

6

Добре застосовувати повернення значення змінній перед її використанням

retval="$?"
if [ $retval -ne 0 ]

Це дозволяє повторно використовувати повернене значення. наприклад, у заяві if if ... elif ... else ...


-1: Ви забули пробіл після [(Без пробілу це стає синтаксичною помилкою bash). Скасується, якщо ви відредагуєте та виправите свою помилку.
саміам

Так, ти маєш рацію. Я полагодив це.
Абдул

@samiam, що останній коментар був спрямований на вас
terdon

4
:) Чи можете ви трохи розширити свою відповідь. Чому це хороша практика? Яких помилок можна було б уникнути?
terdon

1
@terdon це погана практика використовувати $?безпосередньо, тому що це порушиться, якщо ви коли-небудь під час редагування сценарію пізніше будете ставити лінію між командою та $?чеком.
саміам

3

Єдина причина, чому ви хочете використовувати $?в якості аргументів [команди (незалежно від того [, виконується ця команда в умовній частині ifзаяви або ні), це коли ви хочете дискримінувати певний статус повернення, наприклад:

until
  cmd
  [ "$?" -gt 1 ]
do
  something
done

Синтаксис для всіх тих if, while, until... заяви є

if cmd-list1
then cmd-list2
else cmd-list3
fi

Що запускається, cmd-list2якщо cmd-list1це успішно чи cmd-list3іншим чином.

[ "$?" -eq 0 ]Команда є не оп. Він встановлюється $?на 0, якщо $?це 0, і $?на не-нульовий, якщо він був не нульовим.

Якщо ви хочете щось запустити, якщо cmdне вдалося, це:

if ! cmd
then ...
fi

Як правило, вам не потрібно повозитися, $?не кажучи вже про те, що означає trueабо false. Єдині випадки - це, як я вже говорив вище, якщо вам потрібно дискримінувати певне значення або якщо вам потрібно зберегти його на потім (наприклад, повернути його як значення повернення функції), наприклад:

f() {
  cmd; ret=$?
  some cleanup
  return "$ret"
}

Також пам’ятайте, що залишити змінну без котирування - це оператор split + glob. Тут не має сенсу посилатися на цього оператора, тому має бути:

[ "$?" -ne 0 ]

ні [ $? -ne 0 ], не кажучи вже про те, [$? -ne 0 ](який би викликав [команду лише у тому випадку, якщо $IFSвипадково містився перший символ символу $?)

Також зауважте, що спосіб визначення Борна для визначення функції - це дотримуватися function-name()перед командою. Це відбувається в кожному Bourne , як оболонки , за винятком bashі yash(і останні версії posh) , які дозволяють тільки команда з'єднання (з'єднання команди є {...}або (...)чи тому подібним for...done, if...fi...

function foo { ... }є kshсинтаксисом визначення функції. Немає жодної причини, чому ви хочете використовувати його тут.

Ваш код може бути портативно (POSIX) записаний:

foo() (
  cd -P -- "$FOOBAR" || return # what if the cd failed!
  if
    <some command>
  then
    echo 'OK!'
  else
    echo 'Nope!'
  fi
)

Також зауважте, що cdбез -Pмає дуже особливе значення (обробляє шляхи, що містять ..компоненти, інакше, ніж будь-яка інша команда), тому краще включити його в сценарії, щоб уникнути плутанини.

(ця функція повертається, falseякщо cdне вдається, але не, якщо <some command>не вдається).


1

Я вірю, що команда нижче виконає все, що завгодно, в одному рядку.

(( verify = $?!=0?'Nope!':'OK!' ))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.