シェルhere-docの一部としてデータをユーティリティ/コマンドにパイプする方法は?

シェルhere-docの一部としてデータをユーティリティ/コマンドにパイプする方法は?

次の例は、パイプを使用する典型的な方法です。パイプの右側には、標準入力(パイプ)からデータを読み取り、それを標準出力の逆順に印刷する「簡単な」ユーティリティがあります。

{ cat <</EOF
Hello
World
/EOF
} | rev

結果は次のとおりです。

olleH
dlroW

さて、データをシェルの一部であるコマンド/ユーティリティ(ここのドキュメント)にパイプしたいと思います。

{ cat <</EOF
Hello
World
/EOF
} | ksh -x <</EOF
rev
/EOF

これはうまくいきません。エラーメッセージもなく、何もありません。 kshはstdinをhere-docにリダイレクトするため、パイプデータは失われます。

私は一種のリダイレクトを試しました。

set -x
{ cat <</EOF
Hello
World
/EOF
} | { exec 4<&0; ksh -x <</EOF; exec 0<&4 4<&-; }
rev <&4
/EOF

しかし、これも動作しません。 revコマンドにエラーが表示されます。明らかに、stdinからfd4へのリダイレクトはkshの子プロセスに渡されません。

+ exec
+ 4<& 0
+ ksh -x
+ cat
+ 0<< \/EOF
rev <&4
/EOF
+ 0<< \/EOF
Hello
World
/EOF
+ rev
+ ksh[1]: 4: cannot open [Ungültiger Dateideskriptor]
+ exec
+ 0<& 4 4<& -

私はここに閉じ込められています。

私の実際のアプリケーションははるかに複雑です。パイプラインの左側からソースサーバーにsshを実行し、オプションでそのサーバーの他のユーザーにsudoを実行し、tarを実行していくつかのデータファイルをパイプします。パイプの右側では、ターゲットサーバーとしてsshを実行し、オプションで他のユーザーにsudoを実行し、最後にtarを実行してパイプからデータを受信する必要があります。

注:パイプの右側にある文書を使用しないと、すべてがうまく機能します。

{ cat <</EOF
Hello
World
/EOF
} | ksh -xc "rev"

sshとsudo、tarを使用しても正常に動作できます。何らかの理由で私はここの右側にあるシェル文書が欲しいです。ご覧のとおり、kshはこのアプリケーションで私のお気に入りのシェルです。

要求どおり:私の元の声明は次のとおりです。

ssh $SSHOPT $INST_FILE_USER@$INST_FILE_HOST "cd $IDIR && { tar -cvf - $IFIL && echo RCSRC0 >&2 || echo RCSRC1 >&2; }" 2>$TMPFILE.err.src | ssh $SSHOPT $TRG_USER@$TRG_HOST "cd $TDIR && { tar -xmvf - && echo RCTRG0 || echo RCTRG1; }" 1>$TMPFILE.out.trg 2>&1

今まではそんなに良くなった。ご覧のとおり、最小限のエラーチェックが行われ、tarを正常に認識できます。これでsudo機能を追加する必要があります。これは、sshシェルとcdコマンドの間にユーザーを切り替えるためのいくつかのコードを追加する必要があることを意味します。本当にsudoですか?使用されますか?顧客はpbrunを好むことができるので、おそらくpbrunコマンドです。 1つのモンスター説明文の代わりにこの文書を使用すると、これらのさまざまなシナリオをより柔軟に処理できます。

5月21日更新: これまでに提供されたアドバイスに従おうとしましたが、まだ停滞しています。以下の答えを見てみましょう。私は効果的でした。

echo yup | ksh -c 'exec 4<&0; ksh -c "rev <&5" 5<&4'
puy

sshを使用するか、sudoでsshを使用し始めると、リダイレクトの例は機能しなくなります。

echo yup | ssh admin@trg14 ksh -c 'exec 4<&0; ksh -c "rev <&5" 5<&4'
bash: 4: Bad file descriptor

echo yup | ssh admin@trg14 sudo -n -u trg4 ksh -c 'exec 4<&0; ksh -c "rev <&5" 5<&4'
bash: 4: Bad file descriptor

sshとsudoの組み合わせで2つのシナリオを試しました。最初は、kshへの入力としてhere-docを使用することでした。

シーン1:

echo yup | ssh admin@trg14 sudo -n -u trg4 -- ksh<</EOF
whoami && whoami && rev && whoami
/EOF
trg4
trg4
trg4

これまではこれがうまくいきます。注:4つのコマンドはすべてユーザーtrg4として実行されます。ここで、revはパイプを介して期待どおりに入力を受け取りません。

そこで、以下に提案されたソリューションを実装しようとしました。 here-doc は ksh の引数として入力されます。

シナリオ 2:

echo yup | ssh admin@trg14 sudo -n -u trg4 -- ksh -c "$(cat<</EOF
whoami && whoami && rev && whoami
/EOF
)"
trg4
admin
puy
admin

revの出力は予想通りです。しかし、:whoamiはこのバージョン/実装でのみこれを示しています。最初コマンドはsudoユーザーとして実行され、残りのコマンドはsshログインユーザーとして実行されます。 revの場合は問題ありませんが、データベースファイルにアクセスまたは作成する必要がある場合は、権限の問題が原因で失敗します。

この2つの例の結論:シナリオ1は正しいsudoユーザーですべてのコマンドを実行する正しい方法ですが、パイプを使用するにはstdinリダイレクトの問題を解決する必要があります。

助けてくれてありがとう!

答え1

簡単な回避策は、-chere-docスクリプトを内部シェルに引数として渡すことです。

{ cat <<'/EOF'
Hello
World
/EOF
} | ksh -c "$(<<'/EOF'
rev
# you can freely use " inside this script
/EOF
)"

減らす

olleH
dlroW

これも動作しますzsh$(<<$(cat<<どのシェル。

以下を使用する代わりにコマンドレベルで実行している場合は、リダイレクトを操作することもできますexec

... | ksh 4<&0 <<'/EOF'
rev <&4
/EOF

この場合、here-docスクリプトからfdsに戻す方法を使用することもできますが、exec 5<&4 4<&0 <&5 5<&-IMHOはすべて役に立たない合併症です。

kshのスクリプトレベルのリダイレクト

3番目の例の問題は、より簡単なテストケースに縮小できます。

$ echo yup | ksh -c 'exec 4<&0; ksh -c "rev <&4"'
ksh: 4: cannot open [Bad file descriptor]

しかし:

$ echo yup | dash -c 'exec 4<&0; ksh -c "rev <&4"'
puy

kshこれは、()で開かれたすべてのfdにclose-on-execフラグが設定されているためです。O_CLOEXECスクリプトレベルリダイレクト(たとえば、exec 4</path/toまたは使用exec 4<&0)これは、次のために発生します。基準:

execコマンドまたは引数なしで指定され、2より大きい番号のファイル記述子が関連するリダイレクトステートメントを使用して開かれると、シェルが別のユーティリティを呼び出すときにそのファイル記述子が開かれているかどうかは指定されません。

追加のタスクを実行すると、この問題を解決できます。コマンドレベルリダイレクト:

echo yup | ksh -c 'exec 4<&0; ksh -c "rev <&5" 5<&4'
puy

([1]以前のksh93バージョンは使用されていません)を使用してfdを再コピーすると、うまくいきます。mkshR57

$ echo yup | ksh93 -c 'exec 4<&0; ksh -c "rev <&4" 4<&4'
puy

[1] 2019年3月にリリースされ、ここで修正されました。犯罪

答え2

[他の質問をしなければなりません。その他これを積む代わりに質問]

[command]パラメータを使用してSSHを実行します。

次のようにパラメータをssh使用して実行するとどうなりますか?[command]ssh user@host foo bar bazスペース-cスイッチは、リモートシステム上のユーザーのログインシェルに渡される単一のパラメータに変換されます(該当するシェルが実行されている場合は実行さ/bin/sh/bin/sh -c 'foo bar baz'ます)。

今、あなたの例では何が起こりますか?

echo yup | ssh admin@trg14 ksh -c 'exec 4<&0; ksh -c "rev <&5" 5<&4'

sshの引数は文字列で連結されます。

ksh -c exec 4<&0; ksh -c "rev <&5" 5<&4

マシンからシェルを削除すると、最初に実行されます

ksh -c exec 4<&0

それから

ksh -c "rev <&5" 5<&4

2つの別々のコマンドなので、もちろん、2番目のコマンドのfd 4は最初のコマンドでのみリダイレクトされるので、何も指していません。

関連情報