Чому команда `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 / 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? Наскільки я знаю, це лише функція бібліотеки С. Хороший момент, але про rsh. Бути заміною для rsh, що випадає, було запорукою швидкого прийняття в перші дні ssh, коли всі мали тонни сценаріїв, які вже використовують rsh.

Я напевно це використовував у минулому. Дивлячись зараз, це, мабуть, було в HPUX, оскільки, здається, не багато інших Юніонів, я маю визнати, хоча я мав враження, що він більш поширений.
Стефан Шазелас

Спасибі. Це справді допомогло, особливо. оскільки я тунелюю з ssh. Мені довелося зробити ssh -t machine1 'ssh -t machine2 "echo \" 1 && 2 \ ""', щоб отримати "1 && 2" як вихід. Це вбило кілька годин.
ghayes

Якщо ви хочете точно передавати команду, наприклад, з "$ @", ви можете використовувати це: "`printf "%q " "$@"`"з bash's, 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. Наприклад, якщо оболонка для входу є тире і ви хочете виконати команду 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

або це, де ви орієнтуєте на каталог на віддаленому сервері та створюєте його в локальній системі:

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

Список літератури


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\""

Зауважте, що хоча cd /boot && pwdпрацює у всіх оболонках головних сімей (Bourne, csh, rc), він /bin/sh -c "cd /boot && pwd"би не працював, якщо віддалена оболонка є rcсімейством, де "немає спеціальних.
Стефан Шазелас
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.