Почему `if $ (true);тогда ... у тебя получится? - PullRequest
22 голосов
/ 23 января 2012

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

Что должен делать оператор if, если условием является подстановка команды, когда команда не производит вывод?

ПРИМЕЧАНИЕ: Примером является if $(true); then ..., а не if true ; then ...

Например, учитывая:

if $(true) ; then echo yes ; else echo no ; fi

Я бы подумал, что $(true) следует заменить выводом команды true, которая является ничем. Затем он должен быть эквивалентен следующему:

if "" ; then echo yes ; else echo no ; fi

, который печатает no, потому что нет команды, имя которой является пустой строкой, или это:

if ; then echo yes ; else echo no ; fi

, что является синтаксической ошибкой.

Но эксперимент показывает, что если команда не производит вывод, оператор if обрабатывает его как истинное или ложное в зависимости от состояния команды, а не от ее вывода.

Вот скрипт, который демонстрирует поведение:

#!/bin/bash

echo -n 'true:          ' ; if true          ; then echo yes ; else echo no ; fi
echo -n 'false:         ' ; if false         ; then echo yes ; else echo no ; fi
echo -n '$(echo true):  ' ; if $(echo true)  ; then echo yes ; else echo no ; fi
echo -n '$(echo false): ' ; if $(echo false) ; then echo yes ; else echo no ; fi
echo -n '$(true):       ' ; if $(true)       ; then echo yes ; else echo no ; fi
echo -n '$(false):      ' ; if $(false)      ; then echo yes ; else echo no ; fi
echo -n '"":            ' ; if ""            ; then echo yes ; else echo no ; fi
echo -n '(nothing):     ' ; if               ; then echo yes ; else echo no ; fi

и вот вывод, который я получаю (Ubuntu 11.04, bash 4.2.8):

true:          yes
false:         no
$(echo true):  yes
$(echo false): no
$(true):       yes
$(false):      no
"":            ./foo.bash: line 9: : command not found
no
./foo.bash: line 10: syntax error near unexpected token `;'
./foo.bash: line 10: `echo -n '(nothing):     ' ; if               ; then echo yes ; else echo no ; fi'

Первые четыре строки ведут себя так, как я ожидал; линии $(true) и $(false) удивительны.

Дальнейший эксперимент (здесь не показан) показывает, что если команда между $( и ) выдает вывод, ее состояние выхода не влияет на поведение if.

Я вижу похожее поведение (но в некоторых случаях сообщения об ошибках) с bash, ksh, zsh, ash и dash.

Я ничего не вижу в документации bash или в спецификации POSIX "Shell Command Language", чтобы объяснить это.

(Или, может быть, я упускаю что-то очевидное.)

РЕДАКТИРОВАТЬ: В свете принятого ответа, вот еще один пример поведения:

command='' ; if $command ; then echo yes ; else echo no ; fi

или, что эквивалентно:

command=   ; if $command ; then echo yes ; else echo no ; fi

Ответы [ 4 ]

15 голосов
/ 23 января 2012

См. Раздел 2.9.1 спецификации языка .Последнее предложение первого раздела гласит:

Если есть имя команды, выполнение должно продолжаться, как описано в разделе Поиск и выполнение команд.Если имя команды отсутствует, но команда содержит подстановку команды, команда должна завершиться с состоянием выхода последней выполненной подстановки команды.В противном случае команда должна завершиться с нулевым статусом выхода.

$(true) расширяется до пустой строки.Оболочка анализирует пустую строку и обнаруживает, что команда не указана, и следует приведенному выше правилу.

6 голосов
/ 23 января 2012

Есть потенциально два кода выхода для рассмотрения.Во-первых, вот еще два эксперимента, которые должны помочь:

# if $(echo true; false) ; then echo yes ; else echo no ; fi
yes

Внутренняя команда завершается с ошибкой из-за false.Но это не имеет значения, потому что выходные данные команды не пустые, и, следовательно, вместо этого выполняется выход ("true"), а его код выхода имеет приоритет.

# if $(echo false; true) ; then echo yes ; else echo no ; fi
no

Опять же, командная строка внутри $( ) успешен, но вывод - нет, потому что выход («ложь») имеет приоритет.

Состояние выхода команд внутри $( ) имеет значение тогда и только тогда, когда вывод пустой или только пробел.Если выходные данные пусты, список команд для выполнения отсутствует и, следовательно, похоже, что оболочка вернется к состоянию завершения команды inner .

5 голосов
/ 23 января 2012

Похоже, что bash сохраняет статус выхода последней выполненной команды

Это объясняет, почему $(true) и $(false) ведут себя по-разному в тесте if. Они обе выдают пустые команды, которые не считаются выполнением, но имеют разные коды выхода.

Как только вы используете подстановку команды в команде, которая имеет выход, $() пытается выполнить этот вывод как команду, и код выхода , который попытка теперь является последним, использованным для if тест

3 голосов
/ 23 января 2012

Что должен делать оператор if, если условием является подстановка команды, когда команда не производит вывод?

Вывод значения не имеет. Имеет значение код выхода :

   if list; then list; [ elif list; then list; ] ... [ else
   list; ] fi
          The if list is executed.  If its exit status is zero,
          the then list is executed.  Otherwise, each elif list
          is executed in turn, and if its exit status is zero,
          the corresponding then list is executed and the
          command completes.  Otherwise, the else list is
          executed, if present.  The exit status is the exit
          status of the last command executed, or zero if no
          condition tested true.

Если вы замените свои $(echo true) и $(echo false) чем-то другим, вы, вероятно, увидите, что происходит:

$ if $(echo false) ; then echo yes ; else echo no ; fi
no
$ if $(echo command-does-not-exist) ; then echo yes ; else echo no ; fi
command-does-not-exist: command not found
no
$ 

$(..) запускает команду, а затем if выполняет результаты (в данном случае просто true или false или does-not-exist). Пустой $() запускает подоболочку, которая успешно выполняется до завершения и возвращает код выхода 0:

$ if $() ; then echo yes ; else echo no ; fi
yes

Аарон поднял несколько интересных моментов:

$ if $(echo true; false) ; then echo yes ; else echo no ; fi
yes
$ if $(echo 'true ; false') ; then echo yes ; else echo no ; fi
yes
$ if true ; false ; then echo yes ; else echo no ; fi
no

$ if $(echo false ; true) ; then echo yes ; else echo no ; fi
no
$ if $(echo 'false ; true') ; then echo yes ; else echo no ; fi
no
$ if false ; true ; then echo yes ; else echo no ; fi
yes
$ 

Похоже, что при выполнении команды, созданной с помощью подоболочки $(), значение имеет выход из первой команды.

...