tarとsshで複数ファイルやディレクトリを送る/取ってくる
「サーバーにディレクトリ丸っと送りたいんだけど、途中に踏み台サーバーが居て…」よくあります。
「え、ProxyCommand 塞がれてるの??」とセキュリティ堅い環境でよく聞きます。
tar+ssh で大抵回避できるのでコピペで使えるようにまとめました。
tl;dr
ssh -l $STEP_USER $STEP ssh -l $REMOTE_USER $REMOTE tar c $FILES | ssh -l $STEP_USER $STEP ssh -l $REMOTE_USER $REMOTE tar x が分かれば状況に応じて削るだけ。
quote は回避しつつ避けられない場合はがんばりましょう。
direct
to remote:
tar c *.txt | ssh remote.example.com tar x
from remote:
ssh remote.example.com tar c '*.txt' | tar x
over the step
to remote:
tar c *.txt | ssh step.example.com ssh remote.example.com tar x
from remote:
ssh step.example.com ssh remote.example.com tar c '*.txt' | tar x
Prepare
送ったり取ってくるためのサンプルファイルを作ります。
ファイルはなんでもいいのですが「ファイルサイズが小さくて内容の確認が容易で重複しない」要件を満たすために UUID を書き込んだテキストファイルを 2 つ作りました。
❯ uuidgen | tee -a uuid.1.txt
16293D4F-419C-4925-8C88-6C0136F69F14
❯ uuidgen | tee -a uuid.2.txt
17B42188-9445-4DB6-B22F-BA14DC4B5D8D
念のため中身を確認しておきます。
find コマンドは SSH 越しに実行するとオプションのクオートやエスケープに手間がかかり良いサンプルなので、今回は次のように確認しました。
あえて find を使う必要はありませんので状況に応じた適した方法で確認してください。
❯ find . -type f -name 'uuid.*.txt' -print -exec cat {} \;
./uuid.2.txt
17B42188-9445-4DB6-B22F-BA14DC4B5D8D
./uuid.1.txt
16293D4F-419C-4925-8C88-6C0136F69F14
Servers
今回はこの uuid.1.txt と uuid.2.txt を次の 3 つのホストの間でやりとりします。
(localhost)- 特に表記しませんが手元で直接 Terminal を開いている macOS や Linux です
step.example.com- 制約やルールにより経由する必要がある踏み台サーバーです
- これが登場すると急にややこしくなりますね
remote.example.com- 本来ファイルをやりとりしたい Linux などが動いているサーバーです
それぞれ読み替えてください。
tar を使うのでディレクトリをやりとりする場合でもだいたい同じです。
to remote
まずは基本、SSH 越しに送ります。
❯ tar c uuid.*.txt | ssh remote.example.com tar x
処理の流れは次の通りです。
- ローカルで
tar cを実行して作った tar アーカイブをパイプで標準出力に渡します ssh $REMOTEで SSH ログインし、- リモートで
tar xが実行され tar アーカイブが標準入力から読まれて展開されます
もし step.example.com に SSH ログインするユーザー user を指定する場合は、
user@host の形式で次のように指定するか、
❯ tar c uuid.*.txt | ssh user@remote.example.com tar x
または -l user オプションを使って
❯ tar c uuid.*.txt | ssh -l user remote.example.com tar x
のように指定します。
これは ssh コマンドのオプションなので以降すべて共通です。
送ったファイルを確認してみます。
* がローカルで展開されないようにクオートで囲む必要があります。
❯ ssh remote.example.com ls -1 'uuid.*.txt'
uuid.1.txt
uuid.2.txt
ファイルの中身を確認してみましょう。
あえて find を使っていますが、 *, {, }, \ などは見るからにややこしそうですね。
❯ tar c uuid.*.txt | ssh remote.example.com "find . -type f -name 'uuid.*.txt' -print -exec cat {} \;"
./uuid.1.txt
16293D4F-419C-4925-8C88-6C0136F69F14
./uuid.2.txt
17B42188-9445-4DB6-B22F-BA14DC4B5D8D
"" で find コマンドを囲み、 find に渡すファイルパターン uuid.*.txt を '' で囲むことで SSH 越しに実行できます。
ファイルが少ないときは 1 つずつ指定した方がきっと楽です。
❯ ssh remote.example.com cat uuid.1.txt
16293D4F-419C-4925-8C88-6C0136F69F14
❯ ssh remote.example.com cat uuid.2.txt
17B42188-9445-4DB6-B22F-BA14DC4B5D8D
from remote
もう 1 つ基本パターンとして SSH 越しに取ってくる場合です。
❯ ssh remote.example.com tar c 'uuid.*.txt' | tar x
ファイルを送る場合と逆に以下を行なっています。
ssh $REMOTEで SSH ログインし、- リモートで
tar cを実行して作った tar アーカイブをパイプで標準出力に渡し、 - 標準入力から tar アーカイブを読んで
tar xでローカルに展開する
ファイルパターン uuid.*.txt がローカルで展開されないように、先ほど SSH 越しに ls や find を使った時と同じくクオートが必要です。
また今回の例であれば次のように * を \ でエスケープしても実行できます。
❯ ssh step.example.com tar c uuid.\*.txt | tar x
to remote over the step
いよいよ踏み台サーバー登場ですが、次のように要は「踏み台サーバーでもう 1 回 ssh する」だけです。
❯ tar c uuid.*.txt | ssh step.example.com ssh remote.example.com tar x
SSH 越しに ls などを実行するときも考え方は一緒です。
❯ ssh step.example.com ssh remote.example.com ls -1 'uuid.*.txt'
uuid.1.txt
uuid.2.txt
find のようなオプションに記号が豊富なコマンドはちょっとややこしくなります。
❯ ssh step.example.com "ssh remote.example.com \"find . -type f -name 'uuid.*.txt' -print -exec cat {} \;\""
./uuid.1.txt
16293D4F-419C-4925-8C88-6C0136F69F14
./uuid.2.txt
17B42188-9445-4DB6-B22F-BA14DC4B5D8D
考え方は次の通りです。
uuid.*.txtが展開されずにfindコマンドに渡るよう'で囲む
'uuid.*.txt'部分
findコマンドのオプション\などがremote.example.comに渡るよう"で囲む
"find . -type f -name 'uuid.*.txt' -print -exec cat {} \;"部分
(2)がローカルで展開されないよう SSH 接続部分自体を"で囲む
"ssh remote.example.comから最後まで
"が入れ子になるので(2)部分の"を\"とエスケープする
"find . -type f -name 'uuid.*.txt' -print -exec cat {} \;"->\"find . -type f -name 'uuid.*.txt' -print -exec cat {} \;\"
ややこしいですね。
ファイルを 1 つずつ指定できる場合はその方が明らかに楽です。
❯ ssh step.example.com ssh remote.example.com cat uuid.1.txt
16293D4F-419C-4925-8C88-6C0136F69F14
❯ ssh step.example.com ssh remote.example.com cat uuid.2.txt
17B42188-9445-4DB6-B22F-BA14DC4B5D8D
from remote over the step
SSH 越しに踏み台サーバーを経由して取ってくる場合は次の通りです。
❯ ssh step.example.com ssh remote.example.com tar c 'uuid.*.txt' | tar x
remote to remote over the step
ここまでできれば「踏み台サーバー A 越しにサーバー B のファイルを取ってきて、そのままパイプを使って踏み台サーバー C 越しにサーバー D に送る」こともできます。
❯ ssh step.example.com ssh remote.example.com tar c 'uuid.*.txt' | ssh step-2.example.com ssh remote-2.example.com tar xv
appendix: tar options
man tar でマニュアルが読めますが今回使ったオプションだけ紹介します。
tar ctar -c,tar --createでも効果は同じですtar czとzオプションを指定して圧縮することもできます
tar xtar -x,tar --extractでも効果は同じですzオプションを作るときにzオプションを指定して圧縮した場合はtar xzのように展開時にもzオプションを指定できます- が、最近の tar は展開時には
zオプションを指定しなくても勝手に伸張して展開してくれます
- が、最近の tar は展開時には
tar xvのvオプションを指定すると展開されるファイル名が表示されるので安心できます