以下の内容はhttps://blog.ingage.jp/entry/2026/03/09/111925より取得しました。


ssh で複雑なコマンドを実行する

こんにちは、masm11 です。

ssh って使います? ログインするために使ってる人が多いんじゃないですかね。 コマンド実行のために使ってる人はどのくらいいるのでしょう?

ssh mike2

↑のように実行すると、mike2 にログインしますね。

ssh mike2 ls -l

↑のように実行すると、mike2 で ls -l を実行するのみです。

知り合いが、複雑な ssh の実行で困っていましたので、 何故そうなるのか、どうすれば解決できるのか、を解説してみました。

正常パターン

echo foo | gzip | ssh mike2 sh -c zcat

何やってるかわかります?

echo foo で foo という文字列を出力して、それを gzip で圧縮します。 それを mike2 の zcat で復元しています。なので、結果は以下の通りになります。

luna:~ % echo foo | gzip | ssh mike2 sh -c zcat 
foo
luna:~ % 

異常パターン

zcat って gzip -dc に置き換えられますよね。それで置き換えたのが↓です。

echo foo | gzip | ssh mike2 sh -c 'gzip -dc'

さて、実行してみましょう。

luna:~ % echo foo | gzip | ssh mike2 sh -c 'gzip -dc'
 ���`f���31�H5�c���9%
luna:~ % 

化けました…… 何故!?

代案をいくつか

いろいろやってみました。

echo foo | gzip | ssh mike2 gzip -dc
echo foo | gzip | ssh mike2 sh -c 'gunzip -c'

↑このへんは大丈夫でした。

ただ、実際に使いたいコマンドは echo foogunzip -c のみのような簡単なものではありません。 echo foo はあくまでテスト用です。gzip -dc についても、実際のコマンドはパイプを含むため、 sh -c を使う必要があります。

うまく動かない理由

うまく動かないのにはそれなりの理由があります。

ssh mike2 sh -c 'gzip -dc'

ssh には以下の引数が渡されます。

  • ssh
  • mike2
  • sh
  • -c
  • gzip -dc

1つ目はコマンド名ですね。2つ目はホスト名で、3つ目以降がコマンドになります。

ssh は mike2 の sshd に接続し、コマンドを送ります。 そして mike2 の sshd は以下を実行します。

  • zsh
  • -c
  • sh -c gzip -dc

zsh はログインシェルです。私はログインシェルが zsh なので zsh になります。 そして、シェルに -c に続いてコマンド文字列を渡すことで、 そのシェルはそのコマンドを実行してくれます。 こうすることで、シェルが解釈してくれる諸々(環境変数とかパイプとか)を シェルに任せることができます。

ここで、実行されるコマンドは sh -c gzip -dc です。 sh -c の後には一つの引数を渡します。この場合 gzip がそれに該当します。 -dc は sh に渡され、解釈されます。 gzip に渡されて欲しい -dc が sh に渡されてるわけですね。

こうして、sh は gzip を起動し、gzip が入力を圧縮してしまいます。

つまり、文字列 foo を、手元の gzip が圧縮し、それを更に mike2 の gzip が 圧縮しているわけですね。

luna:~ % echo foo | gzip | gzip
gzip: compressed data not written to a terminal. Use -f to force compression.
For help, type: gzip -h
luna:~ % echo foo | gzip | gzip -f
 ���`f���31�H5�c���9%
luna:~ % 

なるほど、化けてはいますが、結果は先程の化けたものと同じのようです。

解決策

では、どうすれば gzip に -dc を渡せるでしょうか?

mike2 の sshd に以下を実行させれば良いですね。

  • zsh
  • -c
  • sh -c "gzip -dc"

そのためには、手元で以下を実行すれば良さそうです。

ssh mike2 sh -c '"gzip -dc"'

試してみました。

luna:~ % echo foo | gzip | ssh mike2 sh -c '"gzip -dc"'
foo
luna:~ % 

良さそうです!

もっと簡単な方法

もっと簡単な方法があります。

これは要するに、手元の実行結果を mike2 上のコマンドに流し込みたい、 その際に通信は gzip で圧縮しておきたい、そういう話であると解釈できます。 それなら、ssh には圧縮機能がありますので、それを使います。

echo foo | ssh -C mike2 cat

ssh に -C を指定することで、通信路を圧縮してくれます。 また、今回の例では mike2 側ではやることがなくなってしまったので、cat にしました。

luna:~ % echo foo | ssh -C mike2 cat
foo
luna:~ % 

まぁうまく行きますよね。 通信路がちゃんと圧縮されてるかどうかを確認することはできませんが、ssh を信じることにしましょう。

今回は以上です!




以上の内容はhttps://blog.ingage.jp/entry/2026/03/09/111925より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14