22

誰でもこの動作を説明できますか? ランニング:

#!/bin/sh
echo "hello world" | read var1 var2
echo $var1
echo $var2

何も出力されませんが、次のようになります。

#!/bin/sh
echo "hello world" > test.file
read var1 var2 < test.file
echo $var1
echo $var2

期待される出力を生成します。

hello
world

パイプは、2 番目の例で test.file へのリダイレクトが行ったことを 1 ステップで行うべきではないでしょうか? ダッシュ シェルと bash シェルの両方で同じコードを試したところ、両方から同じ動作が得られました。

4

9 に答える 9

12

最近追加されbashlastpipeオプションは、パイプラインの最後のコマンドを、ジョブ制御が非アクティブ化されたときに、サブシェルではなく現在のシェルで実行できるようにするものです。

#!/bin/bash
set +m      # Deactiveate job control
shopt -s lastpipe
echo "hello world" | read var1 var2
echo $var1
echo $var2

実際に出力します

hello
world
于 2012-07-26T12:10:13.397 に答える
11
#!/bin/sh
echo "hello world" | read var1 var2
echo $var1
echo $var2

パイプラインは各コンポーネントをサブシェル内で実行するため、出力は生成されません。サブシェルは、親シェルの変数を共有するのではなく、それらのコピーを継承します。これを試して:

#!/bin/sh
foo="contents of shell variable foo"
echo $foo
(
    echo $foo
    foo="foo contents modified"
    echo $foo
)
echo $foo

括弧は、サブシェルで実行されるコードの領域を定義し、$foo はその内部で変更された後も元の値を保持します。

これを試してください:

#!/bin/sh
foo="contents of shell variable foo"
echo $foo
{
    echo $foo
    foo="foo contents modified"
    echo $foo
}
echo $foo

中括弧は純粋にグループ化のためのものであり、サブシェルは作成されません。中括弧内で変更された $foo は、それらの外で変更された $foo と同じです。

これを試してください:

#!/bin/sh
echo "hello world" | {
    read var1 var2
    echo $var1
    echo $var2
}
echo $var1
echo $var2

中括弧内では、read ビルトインが $var1 と $var2 を適切に作成し、それらがエコーされていることがわかります。中括弧の外では、それらはもう存在しません。中括弧内のすべてのコードは、パイプラインの 1 つのコンポーネントであるため、サブシェルで実行されています。

中かっこの間に任意の量のコードを配置できるため、他の何かの出力を解析するシェル スクリプトのブロックを実行する必要があるときはいつでも、このブロックへのパイピング構造を使用できます。

于 2008-09-19T02:20:35.417 に答える
10

これはすでに正しく回答されていますが、解決策はまだ述べられていません。bash ではなく、ksh を使用します。比較:

$ echo 'echo "hello world" | read var1 var2
echo $var1
echo $var2' | bash -s

に:

$ echo 'echo "hello world" | read var1 var2
echo $var1
echo $var2' | ksh -s
hello
world

ksh は優れたプログラミング シェルです。(私の意見では、bash はより優れた対話型シェルです。)

于 2008-08-15T15:52:01.110 に答える
8
read var1 var2 < <(echo "hello world")
于 2008-09-17T00:17:05.340 に答える
6

投稿は適切に回答されていますが、おそらく役立つ可能性のある別のライナーを提供したいと思います.

スペースで区切られた値を echo (または stdout ) からシェル変数に割り当てるには、シェル配列の使用を検討できます。

$ var=( $( echo 'hello world' ) )
$ echo ${var[0]}
hello
$ echo ${var[1]}
world

この例では、var は配列であり、構造体 ${var[index]} を使用して内容にアクセスできます。ここで、index は配列のインデックス (0 から始まります) です。

そうすれば、関連する配列インデックスに割り当てたい数のパラメーターを持つことができます。

于 2008-09-14T17:00:04.750 に答える
6

よし、わかった!

これは見つけにくいバグですが、パイプがシェルによって処理される方法に起因します。パイプラインのすべての要素は、個別のプロセスで実行されます。read コマンドが var1 と var2 を設定すると、親シェルではなく、独自のサブシェルに設定されます。そのため、サブシェルが終了すると、var1 と var2 の値が失われます。ただし、やってみることができます

var1=$(echo "Hello")
echo var1

期待される答えを返します。残念ながら、これは単一の変数に対してのみ機能します。一度に多くの変数を設定することはできません。一度に複数の変数を設定するには、1 つの変数を読み込んで複数の変数に分割するか、次のようなものを使用する必要があります。

set -- $(echo "Hello World")
var1="$1" var2="$2"
echo $var1
echo $var2

パイプを使用するほどエレガントではないことは認めますが、機能します。もちろん、 read はファイルから変数に読み込むことを意図していたので、標準入力から読み込むのは少し難しいはずです。

于 2008-08-05T20:09:15.377 に答える
5

この問題に関する私の見解(Bashを使用):

read var1 var2 <<< "hello world"
echo $var1 $var2
于 2009-03-04T09:52:39.953 に答える
5

これは、パイプ バージョンがサブシェルを作成しているためです。サブシェルは変数をローカル空間に読み込み、サブシェルが終了すると破棄されます。

このコマンドを実行します

$ echo $$;cat | read a
10637

pstree -p を使用して実行中のプロセスを確認すると、メイン シェルから余分なシェルがぶら下がっていることがわかります。

    |                       |-bash(10637)-+-bash(10786)
    |                       |             `-cat(10785)
于 2008-08-05T20:00:43.023 に答える
3

試す:

echo "hello world" | (read var1 var2 ; echo $var1 ; echo $var2 )

複数の人が述べているように、問題は、サブシェルが終了すると破棄されるサブシェル環境で var1 と var2 が作成されることです。上記は、結果がエコーされるまでサブシェルの破棄を回避します。別の解決策は次のとおりです。

result=`echo "hello world"`
read var1 var2 <<EOF
$result
EOF
echo $var1
echo $var2
于 2008-09-17T02:15:03.743 に答える