Сценарий Bash, выполняемый над SSH, возвращается неверный код выхода 0

Я только что столкнулся с этой проблемой при использовании cp -R . /var/ftp/pub/osdirectory и вместо этого использовал (в смонтированном iso/DVD):

find . | cpio -pmd /var/ftp/pub/osdirectory

для корректного копирования файлов

5
18.12.2018, 21:03
3 ответа

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

#!/bin/bash -x

echo 'Unquoted Test:'
ssh evil sh -x -c exit 5 && echo OK || echo FAIL

echo 'Quoted Test 1:'
ssh evil sh -x -c 'exit 5' && echo OK || echo FAIL

echo 'Quoted Test 2:'
ssh evil 'sh -x -c "exit 5"' && echo OK || echo FAIL

Вот результаты:

bash-[540]$ bash -x test.sh
+ echo 'Unquoted Test:'
Unquoted Test:
+ ssh evil sh -x -c exit 5
+ exit
+ echo OK
OK
+ echo 'Quoted Test 1:'
Quoted Test 1:
+ ssh evil sh -x -c 'exit 5'
+ exit
+ echo OK
OK
+ echo 'Quoted Test 2:'
Quoted Test 2:
+ ssh evil 'sh -x -c "exit 5"'
+ exit 5
+ echo FAIL
FAIL

В первом и втором тестах кажется, что 5не передается в exit, как мы ожидали. Это только кажется, что оно исчезает. Не собирается exit, shне жалуется на 5: command not found, а sshне жалуется на это.

В третьем тесте exit 5заключен в кавычки в более крупной команде для запуска на удаленном узле, как и во втором тесте. Это гарантирует, что 5передается в exit, и оба выполняются как опция -cдля sh. Разница между вторым и третьим тестами заключается в том, что весь набор команд и аргументов отправляется на удаленный хост в виде одного аргумента команды ssh.

7
27.01.2020, 20:32

Как отмечено в ответ у вас уже есть, удаленный shне выполняет exit 5. Простоexit:

$ ssh test sh -x -c 'exit 5'; echo $?
+ exit
0

То, что здесь происходит, объясняется, например, в этом ответе:

sshзапускает удаленную оболочку и передает ей строку , а не список аргументов.

Когда мы выполняемssh host sh -c 'exit 5':

  1. Локальная оболочка удаляет одинарные кавычки(удаление кавычек);
  2. Клиент sshполучает аргументы host, sh, -cи exit 5. Он объединяет их в строку и отправляет ее на удаленный хост;
  3. На удаленном хосте sshвызывает оболочку и передает ей строку sh -c exit 5;
  4. Удаленная оболочка вызывает shи передает ей параметр -c, exitв качестве командной строки и 5в качестве имени команды .

Обратите внимание, что если мы добавим слова после exit 5,они просто передаются в shв качестве дополнительных аргументов -нет ошибок, связанных с тем, что они не распознаются оболочкой:

$ ssh test sh -x -c 'exit 5' a b c; echo $?
+ exit
0

straceподтверждает, что 5не является частью командной строки, заданной для sh, здесь; это аргумент:

$ ssh test strace -e execve sh -c 'exit 5'; echo $?
execve("/usr/bin/sh", ["sh", "-c", "exit", "5"], 0x7ffc0d744c38 /* 14 vars */) = 0
+++ exited with 0 +++
0

Чтобы выполнить sh -c 'command'на удаленном хосте, как предполагалось, мы должны быть уверены, что также правильно отправили ему кавычки:

$ ssh test "sh -x -c 'exit 5'"; echo $?
+ exit 5
5

Чтобы было ясно, что цитирование всей удаленной команды не имеет отношения к нашей текущей проблеме, мы могли бы просто написать:

$ ssh test sh -x -c "'exit 5'"; echo $?
+ exit 5
5

Экранирование внутренних кавычек с помощью обратной косой черты вместо двухкратного цитирования также сработает.


Замечание по поводу командыssh host sh -c ':; exit 5'(из комментариев к вашему вопросу ). Что он делает:

$ ssh test sh -x -c ':; exit 5'; echo $?
+ :
5

То есть exit 5выполняется внешней оболочкой, а не sh. Опять же, чтобы shвыйти с нужным кодом:

$ ssh test sh -x -c "':; exit 5'"; echo $?
+ :
+ exit 5
5
8
27.01.2020, 20:32

Другие ответы хороши для ответа на вопрос вместо приведенных примеров. Мое приложение в реальном -мире более сложное и включает ряд сценариев и подпроцессов -. Вот упрощённый -пример сценария, который я хочу выполнить:

#!/bin/bash
sub-process-that-fails
# store and echo returncode for debug purposes
rc=$?
echo $rc
exit $rc

Пытаясь убедиться, что удаленная оболочка на самом деле была bash, а не dash (, как указал @JeffSchaller ), я попытался вызвать скрипт следующим образом:

~$ ssh -t -t host /bin/bash -x /srv/scripts/run.sh ; echo $?

Что привело к этому странному результату:

+ sub-process-that-fails
+ rc=5
+ echo 5
5
+ exit 5
0

После нескольких часов ковыряния я заметил trap 'kill 0' EXITнабор в .bashrc. Это делается для уничтожения всех подпроцессов -в случае, если bash будет уничтожен. трассировка bash, похоже, не отображает выполнение этой ловушки. Я переместил ловушку в скрипт-оболочку. Теперь я вижу, что на самом деле выполняется:

+ trap 'kill 0' EXIT
+ sub-process-that-fails
+ rc=5
5
+ echo 5
+ exit 5
+ kill 0
0

Удаленная оболочка завершает работу с кодом выхода последней команды. Это kill 0и он завершается с 0.

2
27.01.2020, 20:32

Теги

Похожие вопросы