удаленная команда ssh: первый вывод эха потерян - PullRequest
0 голосов
/ 07 ноября 2018

Я пытаюсь запустить несколько команд на удаленном компьютере с помощью вызова ssh 1-liner, указав их в виде строки, разделенной точкой с запятой, которая передается в "bash -c". Это работает для некоторых случаев, но не для других. Проверьте это:

# Note: the "echo 1" output is lost:
bash-3.2$ ssh sandbox bash -c "echo 1; echo 2; echo 3"

2
3

# Note: first echo is ignored again
bash-3.2$ ssh sandbox bash -c "echo 0; echo 1; echo 2; echo 3"

1
2
3

# But when we run other commands (for example "date") then nothing is lost
bash-3.2$ ssh sandbox bash -c "date; date;"
Wed Nov  7 20:27:55 UTC 3018
Wed Nov  7 20:27:55 UTC 3018

Чего мне не хватает?

Удаленная ОС: Ubuntu 16.04.5 LTS
Удаленный ssh: OpenSSH_7.2p2 Ubuntu-4ubuntu2.4, OpenSSL 1.0.2g 1 марта 2016 года

Локальная ОС: macOS High Sierra Versoin 10.13.3
Локальный ssh: OpenSSH_7.6p1, LibreSSL 2.6.2

Обновление: Приведенный выше пример сильно упрощает картину того, что я пытаюсь сделать. Практическое применение состоит в том, чтобы на самом деле генерировать несколько файлов на удаленном компьютере, передавая их в удаленную файловую систему:

#!/bin/bash

A=a
B=b
C=c

ssh -i ~/.ssh/${REMOTE_FQDN}.pem ${REMOTE_FQDN} sudo bash -c \
  "echo $A > /tmp/_a; echo $B > /tmp/_b; echo $C > /tmp/_c;"

После того, как я запустил вышеуказанный скрипт и перешел в удаленное окно, чтобы проверить результаты, я вижу следующее:

root@sandbox:/tmp# for i in `find ./ -name '_*'|sort`; do echo "----- ${i} ----"; cat $i; done
----- ./_a ----

----- ./_b ----
b
----- ./_c ----
c

Как видите, первая команда "echo" сгенерировала пустой файл!

Ответы [ 2 ]

0 голосов
/ 08 ноября 2018

Все аргументы ssh объединяются в одну строку, разделенную пробелами, которая передается в sh -c на удаленном конце. Это означает, что

ssh sandbox bash -c "echo 1; echo 2; echo 3"

приводит к выполнению

sh -c 'bash -c echo 1; echo 2; echo 3'

Обратите внимание на потерю котировок; ssh получил три аргумента bash, -c и echo 1; echo 2; echo 3 после удаления кавычек. На удаленном конце bash -c echo 1 просто выполняет echo, с $0 в оболочке, установленной на 1.

Команда

ssh sandbox bash -c "date; date;"

обрабатывается так же, но теперь первая команда не содержит пробелов. Результат на удаленном конце -

sh -c 'bash -c date; date;'

, что означает, что сначала новый экземпляр bash запускает команду date, а затем команду date, выполняемую непосредственно sh.

В общем, плохая идея использовать неявную конкатенацию ssh. Всегда передавайте команду, которую вы хотите выполнить, как правильно экранированный single аргумент:

ssh sandbox 'bash -c "echo 1; echo 2; echo 3"'
0 голосов
/ 07 ноября 2018

Для ясности, здесь работают 3 оболочки - та, которая интерпретирует ssh, вашу локальную оболочку; тот, который ssh будет автоматически запущен для вас, и bash, который вы вызываете явно.

Причина, по которой «исчезает» 1, состоит в том, что оболочка, которая интерпретирует команду ssh, «съедает» кавычки вокруг аргументов -c, а затем оболочку на другой стороне ssh разбивает аргументы в пробелах. Так что это выглядит как bash -c echo 1 ; echo 2; echo 3. В свою очередь, -c просто получает echo, что отображает пустую строку; 1 становится значением $1 этой оболочки, которое не используется. Затем возвращается внутренний bash, а прямая оболочка ssh нормально запускает echo 2; echo 3.

Учтите это:

$ ssh xxx bash -c "'echo 1'; echo 2; echo 3"
1
2
3

где echo 1 защищено в аргументах ssh, поэтому оболочка ssh 2-го уровня передается bash -c 'echo 1'; echo 2; echo 3. Внутренняя оболочка 3-го уровня эхосигнала 1, а затем оболочка ssh 2-го уровня эхосигналов 2 и 3.

Вот еще одна интересная перестановка:

$ ssh xxx bash -c "'echo 1; echo 2; echo 3'"
1
2
3

здесь, внутренняя оболочка получает все эхо, поскольку они сгруппированы в первой оболочке на " и во второй оболочке на '.

В общем, сценарии оболочки для передачи аргументов сценариям оболочки, которые запускают сценарии оболочки, довольно сложно создавать. Я бы посоветовал вам немного изменить технику, чтобы сэкономить много усилий. Вместо этого вы можете рассмотреть возможность использования конвейера, который позволяет избежать рекурсивной интерпретации оболочки:

$ echo "echo 1; echo 2; echo 3" | ssh -T xxx
1
2
3

(Здесь -T просто для подавления ssh с жалобами на отсутствие псевдотерминала).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...