Используя $? в операторе 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.
Луа
1
@llua Это не бессмысленно. $?содержит состояние последнего конвейера , который не является командой test( [) в ifинструкции. Пример тестирует, some commandбыл ли успешным. Вы можете сделать то же самое с &&и ||, но это может сделать длинные нечитаемые строки по сравнению с if [ $? -eq 0 ]. Тот же самый аргументif some command
бонзаев
@bonsaiviking Я хорошо осведомлен о том, что $?распространяется, я указываю, что нет смысла в testиспользовании; поскольку if some commandделает то же самое с одним утверждением, а не с двумя отдельными утверждениями. если команда уже длинная, добавление еще трех символов не сделает «нечитабельный» ужасно более «нечитаемым».
Луа

Ответы:

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.

aularon
источник
1
Я только что проверил, что ваш ответ правильный на моей Linux Linux VM.
Самам
[это ссылка , testа также
Рикки Beam
2
@RickyBeam в большинстве оболочек [- встроенная оболочка, /usr/bin/[которая используется редко.
Патрик
20

Или вы могли бы $?вообще пропустить . Если ваша команда cmd, следующее должно работать:

function foo {
   (cd $FOOBAR;
   if cmd
   then
      echo "OK!"
   else
      echo "Nope!"
   fi
   )
}
unxnut
источник
6

Рекомендуется присвоить возвращаемое значение переменной перед ее использованием.

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

Это позволяет повторно использовать возвращаемое значение. например, в выражении if ... elif ... else ...

Абдул
источник
-1: вы забыли пробел после [(без пробела это становится синтаксической ошибкой в bash). Отменит, если вы редактируете и исправляете свою ошибку.
Самам
Да, ты прав. Я починил это.
Абдул
@samiam, последний комментарий был направлен на тебя
terdon
4
:) Не могли бы вы немного расширить свой ответ. Почему это хорошая практика? Каких ошибок можно избежать?
Тердон
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содержит первый символ $?).

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

function foo { ... }это kshсинтаксис определения функции. Там нет причин, почему вы хотели бы использовать его здесь.

Ваш код может быть записан (POSIXly):

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!' ))
Jeight
источник