Dlaczego polecenie `cd` nie działa przez SSH?

41

Próbowałem wykonać kopię zapasową niektórych plików przez SSH, ale zamiast tartych, które chciałem, dostałem swój folder domowy. Zrobiłem kilka dalszych testów i sprowadza się to do tego:

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

Który do moich plików list niespodzianka w /rootnie /boot. Ale jeśli uruchomię całe /bin/shpolecenie z terminala, to odpowiednio cds wydrukuje /bootpliki.

Co tu się dzieje?

Ambroz Bizjak
źródło
1
Jeśli nie jest to przykład, dlaczego nie używasz ssh root@server /bin/sh -c "ls -l /boot"?
skromny
To zabawne, o co prosiłeś. Próbowałem to zrobić przed komentowaniem i nadal nie otrzymałem listy katalogów.
unxnut
2
@demure, ponieważ naprawdę chciałem tar, a tar / boot w wyniku uwzględni ścieżkę rozruchową, czego nie chcę
Ambroz Bizjak

Odpowiedzi:

35

ssh nie pozwala ci precyzyjnie określić polecenia, tak jak to zrobiłeś, jako serii argumentów, które mają zostać przekazane do execvp na zdalnym hoście. Zamiast tego łączy wszystkie argumenty w ciąg i przepuszcza je przez zdalną powłokę. Moim zdaniem wyróżnia się to jako poważna wada projektowa ssh ... jest to dobrze zachowujące się narzędzie unixowe pod wieloma względami, ale kiedy przychodzi czas na określenie polecenia, wybrał użycie pojedynczego monolitycznego łańcucha zamiast argumentu argv, jak został zaprojektowany dla MSDOS lub coś takiego!

Ponieważ ssh przekaże twoje polecenie jako pojedynczy ciąg sh -c, nie musisz podawać własnego sh -c. Kiedy to zrobisz, wynik będzie

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

z oryginalnym cytatem utraconym. Tak więc polecenia rozdzielone przez &&:

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

Pierwszy z nich uruchamia powłokę z tekstem polecenia „cd” i $0= "boot". Polecenie „cd” zakończyło się pomyślnie, nie $0ma znaczenia, a /bin/sh -coznacza sukces, a potem się ls -ldzieje.


źródło
2
Chociaż zgadzam się, że to niefortunne, że sshtak się zachowują, gdyby tak nie było, trzeba by to nazwać sexec, nie ssh. sshjest bezpieczną wersją rsh(która zachowywała się pod tym względem) i rlogin( rshwywoływana, rlogingdy nie została przekazana linia poleceń). Szkoda, że ​​nie wdraża się rexecrównież.
Stéphane Chazelas
@StephaneChazelas czy kiedykolwiek było polecenie rexec? O ile wiem, to tylko funkcja biblioteki C. Warto jednak powiedzieć o rsh. Bycie zastępczym dla rsh było kluczem do szybkiego przyjęcia na początku ssh, kiedy wszyscy mieli mnóstwo skryptów już używających rsh.
Zdecydowanie korzystałem z niego w przeszłości. Patrząc teraz, musiało to być na HPUX, ponieważ wydaje się, że nie ma go wiele innych Uników, muszę przyznać, choć miałem wrażenie, że był bardziej rozpowszechniony.
Stéphane Chazelas
Dzięki. To naprawdę pomogło, szczególnie. ponieważ tuneluję za pomocą ssh. Musiałem zrobić ssh -t machine1 'ssh -t machine2 "echo \" 1 && 2 \ ""', aby uzyskać '1 && 2' jako wynik. To zabiło kilka godzin.
ghayes
Jeśli chcesz przekazać polecenie dokładnie, np. Z „$ @”, możesz użyć tego: za "`printf "%q " "$@"`"pomocą bash, printfaby zacytować łańcuch lub łańcuchy z ucieczką powłoki.
Sam Watkins
36

To kwestia cytowania. Ssh już uruchamia polecenie, które przekazujesz w powłoce. Po przekazaniu wielu parametrów są one łączone ze spacją między nimi, aby utworzyć ciąg. Tak więc zdalne polecenie uruchamiane zdalnie to /bin/sh -c cd /boot && ls -l(bez cudzysłowów, ponieważ cudzysłowy w twoim poleceniu zostały zinterpretowane przez lokalną powłokę).

/bin/sh -c cd /booturuchamia się /bin/shi każe mu uruchomić polecenie, cda także ustawić $0na /boot. Po wykonaniu tej czynności uruchomiona jest powłoka nadrzędna (ta uruchomiona przez sshd) ls -l.

W twoim przypadku po prostu usuń ten, sh -cktóry jest całkowicie bezużyteczny, chyba że zdalna powłoka (jak wskazano w /etc/passwdlub w innej bazie danych haseł) nie zrozumie tego polecenia.

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

Jeśli potrzebujesz wywołać inną powłokę, musisz zacytować komendę zdalną, aby chronić ją przed rozszerzeniem przez zdalną powłokę wywoływaną przez sshd. Na przykład, jeśli twoja powłoka logowania to myślnik i chcesz uruchomić polecenie bash:

ssh root@server 'bash -c "cd ~bob && ls -l"'
Gilles „SO- przestań być zły”
źródło
8

Myślę, że ma to więcej wspólnego z tym, jak opcje są analizowane przez powłokę. Na przykład to działa:

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

Ma to ten sam problem, co twoje polecenie:

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

Jeśli włączysz -vprzełącznik ssh, możesz zobaczyć, co się dzieje:

1. polecenie:

debug1: Wysyłanie polecenia: / bin / sh -c "cd / boot && ls -l"

2. polecenie:

debug1: Wysyłanie polecenia: / bin / sh -c cd / boot && ls -l

Zazwyczaj wysyłając polecenia za pośrednictwem ssh, należy zwrócić szczególną uwagę na cytowanie i zawijać cytaty w cudzysłowy, gdy różne warstwy je usuwają. Również nie zawracaj sobie głowy wysyłaniem /bin/sh.

Możesz zrobić bardzo przydatną rzecz, gdy zrozumiesz cytowanie sshtakich jak następujące. Spowoduje to uruchomienie polecenia na zdalnym serwerze, ale zebranie wyników w pliku lokalnie w systemie, w którym uruchomiono sshpolecenie:

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

lub to, gdzie zapisujesz katalog na zdalnym serwerze i tworzysz go w systemie lokalnym:

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

Referencje

slm
źródło
4

Zazwyczaj nie musisz określać, jakiej powłoki użyć. To działa:

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

Ale jeśli twoja domyślna powłoka jest problematyczna, upewnij się, że zacytowałeś całe polecenie, w tym wywołanie powłoki. Każda z poniższych prac

ssh user@host '/bin/sh -c "cd /boot && pwd"'
ssh user@host "/bin/sh -c \"cd /boot && pwd\""
tylerl
źródło
Zauważ, że chociaż cd /boot && pwddziała we wszystkich powłokach głównych rodzin (Bourne, csh, rc), /bin/sh -c "cd /boot && pwd"nie działałby, jeśli zdalna powłoka należy do rcrodziny, w której "nie jest wyjątkowa.
Stéphane Chazelas