こんにちは、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 foo や gunzip -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 を信じることにしましょう。
今回は以上です!