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 c
tar -c
,tar --create
でも効果は同じですtar cz
とz
オプションを指定して圧縮することもできます
tar x
tar -x
,tar --extract
でも効果は同じですz
オプションを作るときにz
オプションを指定して圧縮した場合はtar xz
のように展開時にもz
オプションを指定できます- が、最近の tar は展開時には
z
オプションを指定しなくても勝手に伸張して展開してくれます
- が、最近の tar は展開時には
tar xv
のv
オプションを指定すると展開されるファイル名が表示されるので安心できます