Я только что столкнулся с этой проблемой при использовании cp -R . /var/ftp/pub/osdirectory
и вместо этого использовал (в смонтированном iso/DVD):
find . | cpio -pmd /var/ftp/pub/osdirectory
для корректного копирования файлов
Я могу продублировать это с помощью команды, которую вы использовали, и я могу решить эту проблему, заключив удаленную команду в кавычки. Вот мои тестовые примеры:
#!/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
.
Как отмечено в ответ у вас уже есть, удаленный sh
не выполняет exit 5
. Простоexit
:
$ ssh test sh -x -c 'exit 5'; echo $?
+ exit
0
То, что здесь происходит, объясняется, например, в этом ответе:
ssh
запускает удаленную оболочку и передает ей строку , а не список аргументов.
Когда мы выполняемssh host sh -c 'exit 5'
:
ssh
получает аргументы host
, sh
, -c
и exit 5
. Он объединяет их в строку и отправляет ее на удаленный хост; ssh
вызывает оболочку и передает ей строку sh -c exit 5
; 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
Другие ответы хороши для ответа на вопрос вместо приведенных примеров. Мое приложение в реальном -мире более сложное и включает ряд сценариев и подпроцессов -. Вот упрощённый -пример сценария, который я хочу выполнить:
#!/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.