SSH経由で `cd`コマンドが機能しないのはなぜですか?


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
アンブロス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の安全なバージョンです。実装されていないのは残念です。rshrloginrshrloginrexec
ステファンシャゼラス

@StephaneChazelasにrexecコマンドはありましたか?私の知る限り、これは単なるCライブラリ関数です。ただし、rshについては良い点です。誰もがすでにrshを使用している大量のスクリプトがあったsshの初期の頃に、rshのドロップイン置換であることが迅速な採用の鍵でした。

私は間違いなく過去にそれを使用しました。今見てみると、他の多くのユニックスにはないように見えるので、HPUX上にあったに違いありません。
ステファンシャゼラス

ありがとう。これは本当に役立ちました、特に。sshでトンネリングしているので。出力として「1 && 2」を取得するには、ssh -t machine1 'ssh -t machine2 "echo \" 1 && 2 \ ""' 'を実行する必要がありました。それは数時間を殺しました。
-ghayes

「$ @」などからコマンドを正確に渡したい場合は、これを使用できます"`printf "%q " "$@"`"。bashを使用printfして、1つまたは複数のシェルエスケープ付きの文字列を引用します。
サムワトキンス

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何が起こっているのかを確認できます。

最初のコマンド:

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

参照資料


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ファミリで"はない場合は機能しないことに注意してください。
ステファンシャゼル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.