複数のファイルを標準入力経由で渡す(ssh経由)


15

リモートホストにプログラムがあり、その実行を自動化する必要があります。コマンドは同じマシンでそのプログラムを実行し、次のようになります。

/path/to/program -a file1.txt -b file2.txt

この場合には、file1.txtfile2.txt私はちょうどことはできませんので、プログラム内の全く異なるもののために使用されているcatそれらを一緒に。しかし、私の場合には、file1.txtfile2.txt私はプログラムに渡したいことはありません、私はプログラムを実行する必要があるホスト上で、私のデバイス上にのみ存在します。少なくとも1つのファイルをSSH経由で渡すことができることを知っていますstdin

cat file1.txt | ssh host.name /path/to/program -a /dev/stdin -b file2.txt

しかし、ファイルをホストに保存することは許可されていないので、file2.txtそこにもアクセスする方法が必要です。環境変数の悪用と創造的な使用catsed一緒に使用することで可能になると考えていますが、ツールを使用してこれを達成する方法を理解するのに十分なツールがわかりません。それは実行可能ですか?


2
catそしてsedここに解決策はありません。
スリック

おそらくマウントできますが、プロキシとセキュリティの制約を考えると、それで逃げられるかどうかはわかりません。
グリーンクロークガイ

リモートマシンにsshフォルダーをマウントする権限がありますか?
スリック

1
リモートマシンからローカルセッションへのsshセッションを開くことができれば、ネットワークレベルでSSHフォルダーをマウントしても問題はありません。
スリック

転送できますか?ローカルエンドとリモートエンドにはどのシステムとシェルがありますか?
mosvy

回答:


18

プログラムの引数として指定されたファイルがテキストファイルであり、そのコンテンツを制御できる場合(ファイル内で発生しない行を知っている場合)、複数のヒアドキュメントを使用できます。

{
    echo "cat /dev/fd/3 3<<'EOT' /dev/fd/4 4<<'EOT' /dev/fd/5 5<<'EOT'"
    cat file1
    echo EOT
    cat file2
    echo EOT
    cat file3
    echo EOT
} | ssh user@host sh

catファイル名を引数として使用するサンプルコマンドを次に示します。代わりに:

echo "/path/to/prog -a /dev/fd/3 3<<'EOT' -b /dev/fd/4 4<<'EOT'

EOTそれぞれをそれぞれのファイルで発生しないものに置き換えます。


1
このアプローチは非常に巧妙です!ファイルがテキストでない場合は、base64または適切なol 'uuencodeなどでエンコード/デコードできます。
フィルブランデン

3
いくつかの(ほとんどの)sh実装は一時ファイルを使用してここにドキュメントを実装するため、リモートホストにファイルを保存することが許可されていない場合、OPで機能しない可能性があります。
ステファンシャゼラス

2
dash/bin/shDebianでは、Ubuntuの、など)、 busybox sh/bin/shFreeBSDのからとはyashヒアドキュメントのための一時ファイルを使用しないでください。実際に読み取り専用のchrootでテストしました。もちろん、/dev/fd/ファイルは利用可能でなければなりません-ストックFreeBSDシステムでのみ利用可能/dev/fd/0-2であるためfdescfs、にマウントする必要があり/dev/fdます。
mosvy

1
私は、ASCII情報セパレーターのファンです。これは、妨害なくフィールドを区切るように設計されています。U+001C「情報セパレーター4」(ファイルセパレーター、FS)であり、この場合に理想的ですが、バイナリファイルは偶然にそれを利用する場合があります。したがって、EOT="$(printf $'\x1c')"高度なシェルまたはEOT="$(awk 'BEGIN{printf"%c",28}')"互換性のために提案します。echo "$EOT$EOT$EOT"(バイナリファイルと一致する確率を下げるために3回)など、所定の場所に配置するときに引用符で囲む必要があります。
アダムカッツ

ttyを使用する場合は、なぜttyコマンドを使用しないのですか?私が何かを逃していない限り-私はそれが可能だと思いますか?
プリフタン

14

たぶんあなたが望むものではないかもしれません...しかし、sshによって開かれたパイプを介してtarballを送信することを検討してください。

あなたが言った:

ホストにファイルを保存することはできません。

書き込み可能なホームディレクトリや、ファイルを長期間保存するためのその他の便利な場所がない可能性がありますが、書き込み可能な場所がないとは限りませんtmpfs。特定の接続。

多くのプログラム(さらにlibcルーチン)には書き込み可能が必要な/tmpので、おそらくそれが利用可能になるでしょう。

次に、tarballを一時ディレクトリに解凍し、プログラムを実行し、ssh接続を介してクリーンアップするスクリプトを使用できます。

何かのようなもの:

$ tar cf - file1.txt file2.txt |
  ssh host.name '
      set -e
      tmpdir=$(mktemp -d -t tmp.XXXXXXXXXX)
      cleanup () { rm -rf "$tmpdir"; }
      trap cleanup EXIT
      cd "$tmpdir"
      tar xf -
      /path/to/program -a file1.txt -b file2.txt
  '

これには、ファイルパスに特別な注意が必要な場合があり、考慮すべきいくつかのコーナーケースがあります(それらをテストします)が、一般的なアプローチは機能するはずです。

書き込み可能なディレクトリが利用できない場合、可能なアプローチはprogram、単一の入力としてtarballを取得し、その内容をメモリに展開するように変更することです。たとえばprogram、がPythonスクリプトである場合、組み込みtarfileモジュールを使用すると、そのようなことを簡単に達成できます。


3
それをtarball化する手間をかけなくても、ファイルに書き込み、/tmp/直接使用することを多かれ少なかれ決めました。
グリーンマントガイ

2
単一のssh接続(成功/失敗をテストする必要はありません)を使用し、複数の同時実行が互いに実行される可能性が低い(個別の実行を意図した/ tmpのファイルを使用および/または削除するため) 。)しかし、私はそれが現在存在するsshから得られる最高のものだと思います。幸運を!
フィルブランデン

3
@GreenCloakGuy結局のところ、サーバーにファイルを保存することができます。それに応じて質問を修正してください。
mosvy

1
@mosvy はできますが、そうではありません。質問は「多分、ではなく、私の場合は」ように見える答えた、「すべてのファイルを保存せずにこれを実行することが可能である」で、立っている
グリーンマントガイ

5

TCPリスナーを設定できる場合(より高いポートにすることもできます)、2番目のSSHセッションを使用して、で2番目の入力ソースを確立できますnc

例:

サーバー上にこのスクリプトがあります(~/script.bash):

#!/usr/bin/env bash
cat "$1" | tr a 1
nc localhost "$2" | tr '[:lower:]' '[:upper:]'

また、これらの2つのファイルはローカルにあります。

$ cat a 
aaa
aaa
aaa
$ cat b
bbb
bbb
bbb

次に、最初に2番目のソース($servサーバー)を開始します。

ssh "$serv" nc -l -p 2222 -q1 <b &

そして、適切なコマンドを実行します。

$ ssh "$serv" ./script.bash - 2222 <a
111
111
111
BBB
BBB
BBB
[1]+  Done                    ssh "$serv" nc -l -p 2222 -q1 < b

2

書き込み可能なコメントで確立されている/tmpため、事前にファイルの1つを単純にコピーします。

scp -p file2.txt host.name:/tmp/
ssh host.name "/path/to/program -a /dev/stdin -b /tmp/file2.txt && rm /tmp/file2.txt" < file1.txt

これにより、実行が成功した後にコピーされたファイルもクリーンアップされます(成功に関係なく削除したい場合は&&に変更し;ますが、終了値は失われることに注意してください)。


それが受け入れられない場合は、次の/path/to/programような1つの入力ストリームから2つのファイルを分離できるように、いじくり回すか、ラッパーを提案します。

awk 'FNR == 1 && NR > 1 { printf "%c%c%c", 28, 28, 28 } 1' file1.txt file2.txt \
  | ssh host.name /path/to/tweaked_program

これはASCII情報セパレーター4(ファイルセパレーター、FS)を使用し、3倍にします。これにより、バイナリファイルに偶然その文字列が含まれる可能性を最小限に抑えます。あなたはtweaked_program、その後、セパレータ与えられた入力を分割して、変数などの2つの保存されたファイル上で動作します。

もちろん、tarballを処理するライブラリを備えた言語を使用している場合、より安全でクリーンなアプローチは、次のようtarなコードにパイプするsshことです。

tar -zpc file1.txt file2.txt |ssh host.name /path/to/tweaked_program

そしてtweaked_program、アーカイブを解凍して開き、各ファイルを異なる変数に保存してから、変数に対して元programののロジックを実行します。


1

経由sshでポートを転送することが許可さwgetれており、リモートマシンとbusyboxローカルマシンにアクセスできる場合、次のようなことができます。

mkdir /tmp/test; cd /tmp/test
echo 1st_file > 1st_file
echo 2nd_file > 2nd_file

busybox httpd -f -p 127.0.0.1:11080 &
ssh USER@HOST -R 10080:127.0.0.1:11080 '
        cat <(wget -q -O- http://localhost:10080/1st_file) \
            <(wget -q -O- http://localhost:10080/2nd_file)
'
kill $!

cat2つのファイル引数を取るプログラムの例として使用)。

経由-Rでポートを転送する機能のみが必須です。httpを実行する代わりに、他の方法を使用できます。あなたの場合は netcatサポート-dおよび-Nオプション:

nc -Nl localhost 11001 < 1st_file &
nc -Nl localhost 11002 < 2nd_file &
ssh USER@HOST -R 10001:localhost:11001 -R 10002:localhost:11002 '
        cat <(nc -d localhost 10001) <(nc -d localhost 10002)

<(...)リモートマシンのログインシェルがksh-またはbash-でない場合、プロセスの置換を置き換える方法があります。

全体として、これはあまり大きくありません。正確なsystem / shells / config / permissions(提供していません)についてのより良い知識は、よりスマートなソリューションを可能にします。


-1

更新:これは実際には機能しません。sshはstdinとstdout / stderrの意味について非常に明確な考えを持っており、stderrからの読み取りを実際に許可していません。この回答は機能しないため、数日後に削除します。非常に興味深い議論をありがとう!!!


残念ながら、sshクライアントはサーバーに3つのファイル記述子(stdinstdoutおよびstderr)しか渡さず、追加のファイル記述子を渡すための規定がないため、直接これを行う素晴らしい方法はありません(この特定のユースケースに役立ちます) )

(また、SSHプロトコルには追加のファイル記述子を渡す規定があり、sshクライアントのみがその機能を使用する方法を実装していないことに注意してください。理論的には、この機能を公開するにはクライアントをパッチで拡張するだけで十分です)

一つハックあなたが探しているものを達成する方法は、ファイルディスクリプタ2(使用することであるstderr第二のファイルを渡すためにファイルディスクリプタを)。何かのようなもの:

$ ssh remote.name \
      /path/to/program -a /dev/stdin -b /dev/stderr \
      <file1.txt 2<file2.txt

これは機能するはずですprogram。stderrに書き込もうとすると問題が発生するだけです。プログラムを実行する前にリモートエンドでファイル記述子を再ジャグリングすることにより、この問題を回避できます。file2.txtファイル記述子3に移動し、stderrを再度開いてstdoutをミラーリングできます。

$ ssh remote.name \
      /path/to/program -a /dev/stdin -b /dev/fd/3 \
      '3<&2' '2>&1' \
      <file1.txt 2<file2.txt

私はこれを実際にテストしていません(電話で入力する)が、少なくとも理論的には機能すると期待しています。何らかの理由でそうでない場合はお知らせください。乾杯!
フィルブランデン

それはまったく機能しません。ハッキングされたsshでもありません。AFAIK stderrは個別のチャンネルを使用せず、特別な種類のメッセージを使用します。しかし、はい、パイプまたは無名のUNIXドメインソケットとしてsshチャネルにアクセスできることは(ソケット転送を行う代わりに)素晴らしいことであり、それはいかなる種類または形でも可能ではないようです。
mosvy

リモートコマンドの@ mosvy、fd 0、1、および2は、3つの異なるパイプになります。データはssh接続で多重化された3つの異なるチャネルを通過しますが、これらは単方向(fd 0ではクライアントからサーバーへ、1および2ではサーバーからクライアントへ)であるため、パイプが双方向のシステム上でも、仕事。Linuxでは、/ dev / stderrを読み取り専用モードで開くと、パイプのもう一方の端が表示されるため、どちらも機能しません。
ステファンシャゼラス

最後の1がremote.name上のユーザのログインシェルを前提としていますは、Bourne-のようなものです(3<&2Bourneシェルの演算子であり、そしてsshdはクライアントから送信されたコマンドを解釈するために、ユーザのログインシェルを実行)
ステファンChazelas

2
@StéphaneChazelasいいえ、単一のsshチャネルがstdin / stdout / stderrに使用され、stderrデータはSSH_MSG_CHANNEL_EXTENDED_DATAタイプがに設定されたメッセージを介して転送されSSH_EXTENDED_DATA_STDERRます。ここで
-mosvy
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.