複数の入力ファイル/ストリームを単一のストリーム(仮想コマンドを使用stream-cat
)に接続し、そのストリームをリモートホストにパイプした後、この例のようにリモートssh
ホスト()から別のファイル/ストリームに再び分離しようとしています。stream-sep
目的のみ:
stream-cat <( zfs send tank/vm@snapshot ) somefile.txt | ssh user@host "stream-sep >( zfs receive tank/vm@snapshot ) somefile.txt"
説明 例:zfs send
サイズがあらかじめ分からない大きな文字列のデータを出力します (そのためtar
処理できません)。このデータストリームは通常のファイルの内容に関連付けられていますsomefile.txt
。結果のストリームはパイプで接続され、ssh
再び分離されます。最初のストリームはにパイプされ、zfs receive
2番目のストリームは通常のファイルに書き込まれます。
これらのプログラムは、検索できないストリームをチャンクに読み込み、常にチャンクサイズを書き込んでから、ストリームの終わりに達するまでデータを記録して実装しやすくする必要があります。オーバーヘッドは最小化されます。
そのようなプログラムはすでに存在していますか?
答え1
あなたが説明するもの再利用;何かが必要規約(つまり、データ処理方法の公式仕様)
これを達成する方法はいくつかあります。たとえば、コンピュータが同じサーバーでもHTTP経由で複数のファイルを同時に完全にダウンロードできることがわかります。この機能は、異なるストリームを送信して受信側で「分離」できるトランスポートプロトコルとして機能するTCPによって最初に導入されました。
したがって、TCPはすでにこの機能を提供しているので、2つの同時SSH接続を開始して使用するだけです!
zfs send | zstd -10 | ssh user@host 'zstd -d | zfs receive tank/vm@snapshot' &
# ^ ^ ^ ^ ^ ^ ^ ^
# | | | | | | | |
# | | | | | | | Tell your own shell to run
# | | | | | | | this in the background and
# | | | | | | | not block
# | | | | | | |
# | | | | | | |
# | | | | | | \---- Program to receive in the end
# | | | | | |
# | | | | \-----+-------- use zstd to decompress
# | | | | received data
# | | | |
# | | | |
# | | | \---------------------------- our first ssh invocaton
# | | |
# | \----+---------------------------------- use zstd to compress at medium
# | high compression level (10)
# |
# \------------------------------------------------- the first program whose output
# we send
cat somefile.txt | ssh user@host 'cat > somefile.txt'
# Second SSH connection
もちろん、少し優雅なものではなくcat somefile.txt | ssh … > somefile.txt
直接使用することもできますscp somefile.txt user@host:somefile.txt
(内部的にはSSHを使用しますが、シェル接続を実行する代わりにSSHに組み込まれているSCPレイヤーを使用してファイルをコピーします)。
ファイルに以下を追加すると、2番目の接続をすばやく作成できます~/.ssh/config
。
ControlMaster auto
ControlPath /tmp/.ssh-socket-%h_%p_%r
これは、SSHがSSHセッションを再利用し、複数の暗号化ストリームを同時に送信するように指示します(これはおよびscp
すべての組み合わせで機能します)。ssh
答え2
stream-cat
おおよその実装は次のようstream-sep
に簡単に書くことができますperl
。
stream-cat() {
perl -ne 'BEGIN{$/ = \0x7fff}
print pack("n", $c|length()<<1), $_;
$c = !eof' -- "$@"
}
stream-sep() {
perl -e 'while($/ = \2, $_ = <STDIN>) {
$n = unpack "n";
open OUT, shift@ARGV unless $n & 1;
if ($n>>=1) {$/ = \$n; $_ = <STDIN>; print OUT}
}' -- "$@"
}
または関数の#! /bin/sh -
代わりにスクリプトと同じです。sh
(エラー処理は、読者の皆さんの練習問題として残しました:-).
stream-cat
ネットワークエンコーディング(ビッグエンディアン)短い形式が先頭に32767バイトのレコードを送信します。n
これらのうち最も低いビットは新しいストリーム(0)の開始か連続かを示し、残りのビットはサイズです。
次に、例えば次のようになります。
$ cat a
test
$ stream-cat 'seq 10|' a | stream-sep '|wc -l' '>b'
10
$ cat b
test
あなたの場合は次のとおりです。
stream-cat 'zfs send tank/vm@snapshot|' somefile.txt |
ssh user@host 'stream-sep "|zfs receive tank/vm@snapshot" ">somefile.txt"'
まれに安全でない形式open
(ここでは<>
asとasとしても使用されます-n
)が実際に役に立ち、ファイルを読み取り専用または書き込み専用モードで開くか、コマンドにパイプするか、コマンド<file
からパイプすることができます。>file
|cmd
cmd|
これを使用する方が|cmd
/あなたよりも優れています/cmd|
一度に1つだけ開いているので、問題なく数千の別々のストリームを送信できます。<(cmd)
>(cmd)