1

У меня есть следующая простая команда оболочки, которая, как я ожидаю, потерпит неудачу, и она работает на моем локальном компьютере:

$ DIR=$(false) && echo ok || echo fail
fail
$ sh -c 'DIR=$(false) && echo ok || echo fail'
fail

Но когда я передаю эту команду через ssh, она больше не работает, как ожидалось:

$ ssh user@host sh -c 'DIR=$(false) && echo ok || echo fail'
ok

Так что я не совсем уверен, где проблема. Я уже использую апостроф, чтобы избежать слишком раннего расширения переменных.

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


Еще одна аномалия, которую я нашел на следующем примере:

$ ssh user@host sh -c 'echo 1; echo 2;'

2

который печатает только 2, вместо печати как 1, так и 2.

2 ответа2

2

Я думаю, что проблема в том, что здесь задействованы три оболочки:

  1. локальная оболочка, которая обрабатывает команду ssh ;
  2. удаленная оболочка, которую ssh вызывает для обработки команды на целевом хосте;
  3. дополнительная оболочка, вызываемая sh -c в удаленной команде.

Цитаты делают две вещи:

  • они останавливают локальную оболочку, вычисляя любые переменные в локальной оболочке (1);
  • они проверяют наличие единственного параметра для удаленного sh -c .

Таким образом, ssh видит четыре параметра: user@host , sh , -c и требуемую команду. Однако кавычки удаляются при построении четвертого параметра, и когда удаленная оболочка (2) получает свои параметры, она интерпретирует сам $(false) , и код завершения устанавливается в его собственной среде перед вызовом subshell (3).

Таким образом, дополнительная оболочка (3) видит DIR= && echo ok || echo fail и DIR= - абсолютно корректная, безошибочная команда, поэтому ветка ok .

Вы можете увидеть тот же эффект в:

sh -c 'sh -c "DIR=$(false) && echo ok || echo not"'

или же:

sh -c "sh -c 'DIR=$(false) && echo ok || echo not'"

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

sh -c 'sh -c '\''DIR=$(false) && echo ok || echo not'\'

Пример Ричарда работает, потому что все раскрытие выполняется в единственной удаленной оболочке, и первоначальное false; не влияет на последующую команду. Это не имеет ничего общего с темой, обсуждаемой в его ссылке.

1

Это должно работать как ожидалось:

ssh user@host 'DIR=$(false) && echo ok || echo fail'

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

Странно, это также работает как ожидалось:

ssh user@host sh -c 'false; DIR=$(false) && echo ok || echo fail'

Я не уверен на 100%, что здесь происходит, но это может быть связано с этим вопросом: https://unix.stackexchange.com/questions/126938/why-is-setting-a-variable-before-a-command- юридико-в-баш

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .