2008-08-05 9 views
16

誰もこの動作を説明できますか? 実行:シェルスクリプトの入力リダイレクトの奇妙

hello 
world 

パイプは何リダイレクトが1つのステップで行うべきではありません。

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

が予想される出力を生成します。一方、何もされて出力に含ま中

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

結果、 test.fileは2番目の例で行いましたか?私は、ダッシュとbashの両方のシェルで同じコードを試して、両方から同じ動作を得ました。

答えて

8

bashに最近追加がパイプラインの最後のコマンドは、現在のシェルで実行することができますlastpipeオプション、である、ないサブシェル、ジョブ制御が無効化されたとき

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

ます実際に出力

hello 
world 
3

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

は、このコマンドに

$ echo $$;cat | read a 
10637 

を実行し、実行中のプロセスを見てpst​​reeは-pを使用し、あなたのメインシェルのオフにぶら下がって余分なシェルが表示されます。

|      |-bash(10637)-+-bash(10786) 
    |      |    `-cat(10785) 
5

大丈夫、わかった!

これはキャッチするのは難しいバグですが、パイプがシェルによって処理される方法から発生します。パイプラインのすべての要素は別のプロセスで実行されます。読み込みコマンドがvar1とvar2を設定すると、それはそれ自身のサブシェルであり、親シェルではありません。したがって、サブシェルが終了すると、var1とvar2の値は失われます。ただし、予想される答えを返し

var1=$(echo "Hello") 
echo var1 

やってみることができます。残念ながら、これは単一の変数に対してのみ機能し、一度に多くの変数を設定することはできません。あなたは一つの変数に読み込まれ、複数の変数にそれを切り刻むか、このような何かを使用する必要があります一度に複数の変数を設定するために:

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

私はそれがパイプを使用してのようにエレガントではありません認めるが、それは動作します。もちろん、読み込みはファイルから変数に読み込むことを意図していたので、標準入力から読み込むことはやや難しいはずです。

+2

これは、シェルの選択に依存します。 Ksh93はプロセスのオーバーヘッドを制限するように設計されています。また、呼び出し元のシェルプロセス内のパイプライン*の最終要素を実行し、それによって状態を保持します。 –

+1

Bash 4.2では、同じことをするオプションが導入されました。ジョブ制御( 'set + m')を止め、' lastpipe'オプションを設定します( 'shopt -s lastpipe')。 – chepner

+0

私はそれを調べます。ありがとう! –

9

これは既に正しく回答されていますが、その解決策はまだ述べられていません。 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はより良いインタラクティブなシェルです)。

3

投稿には適切な回答がありましたが、多分何らかの使用が可能な代替ライナーを提供したいと思います。

変数をシェルに(そのことについてまたはSTDOUT)エコーからスペース区切り値を割り当てるため、シェル・アレイを使用することを検討して可能性は、この例varに

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

が配列され、コンテンツが使用してアクセスすることができます$ {var [index]}のコンストラクタ。ここで、indexは配列インデックス(0から始まる)です。

このように、関連する配列インデックスに割り当てたいパラメータをいくつでも指定することができます。

+2

良い溶液。これはbashでうまく動作しますが、ダッシュシェルでは機能しません。 –

8
read var1 var2 < <(echo "hello world") 
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 
10
#!/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 

を、組み込みの読み取りが正常に$ VAR1とVAR2 $を作成し、あなたは彼らが反響を受けることがわかります。中括弧の外側には、もう存在しません。中括弧内のすべてのコードはサブファイルで実行されています。これは、パイプラインの1つのコンポーネントであるです。

中括弧の間に任意の量のコードを入れることができるので、何か他のものの出力を解析するシェルスクリプトのブロックを実行する必要があるときはいつでも、このパイピングブロックを使用できます。この問題に関する

+1

+1のきれいな例 - 素敵で読みやすいコード –

4

私のテイク(使用してバッシュ):

read var1 var2 <<< "hello world" 
echo $var1 $var2