Почему команда `cd` не работает через SSH?

41

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

ssh root@server /bin/sh -c "cd /boot && ls -l"

Который к моему удивлению перечисляет файлы в /rootнет /boot. Но если я запускаю всю /bin/shкоманду из терминала, она корректно cdраспечатывает /bootфайлы.

Что тут происходит?

Амброз Бижак
источник
1
Если это не пример, почему вы не используете ssh root@server /bin/sh -c "ls -l /boot"?
скромно
Забавно, что ты спросил. Я пытался сделать это, прежде чем комментировать, и до сих пор не получил список каталогов.
unxnut
2
@demure, потому что я действительно хотел tar, а tar / boot будет включать в результат путь загрузки, который мне не нужен
Ambroz Bizjak

Ответы:

35

ssh не позволяет вам точно указывать команду, как вы это сделали, в виде последовательности аргументов, передаваемых execvp на удаленном хосте. Вместо этого он объединяет все аргументы в строку и запускает их через удаленную оболочку. На мой взгляд, это серьезный недостаток дизайна в ssh: в большинстве случаев это хорошо работающий инструмент unix, но когда приходит время указать команду, она выбрала одну монолитную строку вместо argv, например это было разработано для MSDOS или что-то!

Поскольку ssh передаст вашу команду в виде одной строки sh -c, вам не нужно указывать свою собственную sh -c. Когда вы делаете, результат

sh -c '/bin/sh -c cd /boot && ls -l'

с оригинальным цитированием потерял. Таким образом, команды, разделенные &&:

`/bin/sh -c cd /boot`
`ls -l`

Первый из них запускает оболочку с текстом команды "cd" и $0= "boot". Команда "cd" завершается успешно, не $0имеет значения, и /bin/sh -cуказывает на успех, затем ls -lпроисходит.


источник
2
Хотя я согласен, что, к сожалению, так sshсебя вести, если бы это было не так, его нужно было бы назвать sexec, а не ssh. sshявляется безопасной версией rsh(которая вела себя то же самое в этом отношении) и rlogin( rshназывается , rloginкогда не передается из командной строки). Просто прискорбно, что это также не реализуется rexec.
Стефан Шазелас
@StephaneChazelas была ли когда-нибудь команда rexec? Насколько я знаю, это просто функция библиотеки C. Хороший вопрос, хотя, о rsh. Быстрая замена rsh была ключом к быстрому внедрению ssh, когда у всех были тонны скриптов, уже использующих rsh.
Я определенно использовал это в прошлом. Теперь, должно быть, он был на HPUX, так как, кажется, его не так много у других Unix, я должен признать, хотя у меня сложилось впечатление, что он был более распространенным.
Стефан Шазелас
Спасибо. Это действительно помогло, особенно так как я туннелирую с ssh. Мне пришлось сделать ssh -t machine1 'ssh -t machine2 "echo \" 1 && 2 \ ""', чтобы получить '1 && 2' в качестве вывода. Это убило несколько часов.
Гэйс
Если вы хотите точно передать команду, например, из "$ @", вы можете использовать это: "`printf "%q " "$@"`"с bash printfдля цитирования строки или строк с экранированием оболочки.
Сэм Уоткинс
36

Это вопрос цитирования. Ssh уже запускает команду, которую вы передаете в оболочке. Когда вы передаете несколько параметров, они объединяются с пробелом между ними для построения строки. Таким образом, удаленная команда, которую вы запускаете удаленно /bin/sh -c cd /boot && ls -l(без кавычек, потому что кавычки в вашей команде были интерпретированы локальной оболочкой).

/bin/sh -c cd /bootзапускает /bin/shи говорит ему выполнить команду, cdа также установить $0в /boot. После этого запускается родительская оболочка (запущенная пользователем sshd) ls -l.

В вашем случае просто удалите тот, sh -cкоторый совершенно бесполезен, если ваша удаленная оболочка (как указано в /etc/passwdдругой базе паролей) не понимает эту команду.

ssh root@server "cd /boot && ls -l"

Если вам нужно вызвать другую оболочку, вы должны указать удаленную команду в кавычках, чтобы защитить ее от раскрытия, вызванного удаленной оболочкой sshd. Например, если ваша оболочка входа в систему dash и вы хотите выполнить команду bash:

ssh root@server 'bash -c "cd ~bob && ls -l"'
Жиль "ТАК - прекрати быть злым"
источник
8

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

$ ssh root@server /bin/sh -c '"cd /boot && ls -l"'

Это та же проблема, что и ваша команда:

$ ssh root@server /bin/sh -c 'cd /boot && ls -l'

Если вы включите -vпереключатель, sshвы сможете увидеть, что происходит:

1-я команда:

debug1: отправка команды: / bin / sh -c "cd / boot && ls -l"

2-я команда:

debug1: отправка команды: / bin / sh -c cd / boot && ls -l

Как правило, при отправке команд через них sshвы должны обращать особое внимание на цитаты и заключать их в кавычки, поскольку различные слои удаляют их. Также не беспокойтесь об отправке /bin/sh.

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

$ ssh root@server 'free -m' > /tmp/memory.status

или это, где вы tar каталог на удаленном сервере и создать его в локальной системе:

$ ssh remotehost 'tar zcvf - SOURCEDIR' | cat > DESTFILE.tar.gz

Ссылки

SLM
источник
4

Обычно вам не нужно указывать, какую оболочку использовать. Это работает:

ssh user@host "cd /boot && pwd"

Но если ваша оболочка по умолчанию проблематична, просто убедитесь, что вы заключили в кавычки всю команду, включая вызов оболочки. Любая из следующих работ

ssh user@host '/bin/sh -c "cd /boot && pwd"'
ssh user@host "/bin/sh -c \"cd /boot && pwd\""
tylerl
источник
Обратите внимание, что хотя cd /boot && pwdработает во всех оболочках основных семейств (Bourne, csh, rc), /bin/sh -c "cd /boot && pwd"не будет работать, если удаленная оболочка принадлежит к rcсемейству, где "не является особенным.
Стефан Шазелас