コマンド置換を実行すると表示されないのはなぜですか?

コマンド置換を実行すると表示されないのはなぜですか?

cd親プロセスで使用されるパスを返す対話型スクリプトがあります。このように:

$ cd $(myscript)

スクリプトの実行中に使用したいと思いますless。残念ながら、コマンドの置換中に を使用することはうまくlessいかないようです。

$ echo $(less <<< 'some text') # Not working
some text

だから私の質問は:なぜ動作しないのですか?解決策はありますか?

スクリプトの実行中に対話型ヘルプ画面を表示しようとしています。このスクリプトをコマンド置換に入れる必要があります。これは、親プロセスで作成されたパスにCDを移動する唯一の方法であるためです。より良い方法があるかもしれませんが、これが最善の選択です。

答え1

echo $(somecmd)通常、すべてのsomecmd.の場合、出力はsomecmdの引数に展開され、その引数を取得echoechoて出力に印刷します。通常は直接実行できますsomecmd

ワードセパレータとワイルドカードの2つの違いは一般的です。echo $(somecmd)スペースを圧縮し、ファイル名glob()のように見えるすべての項目を展開します*.txt。しかし、もちろん、これは単に実行するかsomecmd引用echo "$(somecmd)"符で実行するときには発生しません。それ最後の改行文字を除くすべての項目を削除します。拡張バックスラッシュをエスケープするechoこともできます。

lessの目的は次のとおりです。インタラクティブファイルを参照するか、端末がほとんど必要なパイプ入力を実行します。コマンド置換で実行すると、 の出力がlessパイプに接続され、その動作が . cat(マンページで正確な動作に関する言及が見つからないようです)


これで、コマンドオーバーライドで実行され、ユーザーがいくつかの出力を参照できるようにし、コマンド置換で別のテキストを返すスクリプトが必要な場合は、リダイレクトをクリエイティブに使用できるようになります(mosvyの説明)。

$ cat myscript
#!/bin/bash
echo this is the output

cat >&2 <<EOF
you can
browse this
with less
EOF
$ foo=$( ./myscript 2> >(less > /dev/tty))

スクリプトの標準出力はコマンドオーバーライドに移動し、エラー出力はに設定されるために設定さlessfooますthis is the outputfoo少なくともless

さらに、これらの実行はlessジョブ制御を混乱させるので、実行中にCtrl-Cを押すと奇妙な動作が発生する可能性があります。


ここでは一時ファイルを使用することをお勧めします。結果をパラメータ(たとえば)myscriptというファイルに書き込み、次のコマンドを実行します。echo "$result" > "$1"

tmp=$(mktemp)
myscript "$tmp"
cd "$(cat "$tmp")"
rm "$tmp"

あるいは、他の対話型プログラムをmyscript一般的な方法で起動することができ、タスクless制御に問題がないはずです。

答え2

(trap : INT; echo "$(less <<< 'some text' >/dev/tty; echo done)")

lesscatコマンドの置き換え(標準出力がパイプの場合)に使用されるときなど、標準出力がttyでない場合は、何か(非)クールになります[1]。

対話型bashでテストすると(ジョブ制御はデフォルトで有効になっています)、(...)サブシェルはコマンドが端末で正しいフォアグラウンドジョブの一部として実行されることを確認します。そうしないと、次のコマンドを使用してジョブを一時停止して終了することがlessできない可能性があります。誤った行動は異なる方法で起こります[2]。^Z^C

これにより、trap : INTサブシェルはaを無視し、^Cその構成をそのサブシェルに渡さないようにします。

コマンドがシェルスクリプトの一部である場合、すべてのプロセスは同じプロセスグループ/タスクの一部として実行されますが、(...)トラップの範囲を制限することはまだ役に立ちますINT

[1]標準出力がttyでない場合は、きれいにエスケープされたバイナリデータを印刷するためにも使用できません。

$ printf '\xee' | less -FXR
<EE>
$ printf '\xee' | less -FXR | cat
�$ 

[2]起こり得ることが多く、すべて悪い。例えば、

$ ls -d $(less <<<'some text' >/dev/tty; pwd)

bash別のプロセスをフォークしてバイナリを実行し、プロセスの置き換えを子プロセスとしてls実行します。$(...)今後別のプロセスグループ/タスクに移動し、バイナリを実行します。

^Z内側を押すとless正常に処理されますが、停止lessできないか(シェルとその操作がlessセッションリーダーの場合)、停止するだけでless親プロセスは停止しません。

^Cinside を押すとless捕捉され処理されますが、lessa メインシェル(ターミナルのフォアグラウンドジョブ)に転送され、bash の readline および race だけでSIGINTなく次のプロンプトに戻ります。less端末属性の変更はお互いを認識せず、完全な混乱を招きます。

ノート

これらの内容はすべて以下に適用されます。どのプログラムは例だけでなく、コマンドの置き換えによって実行されます$(less ...)

$ cat >foo <<'EOT'; chmod 755 foo
t=$(mktemp)
vi "$t" </dev/tty >/dev/tty 2>&1
echo "$t"
EOT
$ cat $(./foo)
# ^Z doesn't work in vi

関連情報