
2行を2つの変数として読み込もうとしています。 Bashでは、次のようなものを使用します。
cat << EOF > myfile
line1
line2
EOF
cat myfile | {
read firstline
echo $firstline # "line1" in bash and sh
read secondline
}
echo $firstline # "line1" in bash, empty in sh
echo $secondline
ただし、Bourne Shell では、$firstline
コマンド$secondline
グループの外部では空です。 shでどうすればいいですか?
答え1
firstline
(テストでコードをテストするときにこれを設定した可能性が高く、bash
その値は最終的に空でなければなりません。)
パイプラインを実行するとき
cat myfile | { read firstline; read secondline; }
右側はサブシェルで実行されます。{ ...; }
これはパイプラインの一部ではないからです。サブシェル環境には 2 つの変数firstline
sum secondline
(両方を読み取った後) が含まれますが、サブシェルが終了すると、両方の変数が削除され、削除されます。
これはPOSIXsh
とbash
。
(4.2+)では、bash
シェルオプションを設定することでこの問題を解決できますlastpipe
。bash
マニュアルから:
パイプラインの各コマンドは、別々のプロセス(つまりサブシェルで)で実行されます。サブシェル環境の説明については、コマンド実行環境を参照してください。
lastpipe
組み込みオプションを使用して有効にするとshopt
(以下の説明を参照shopt
)、パイプの最後の要素がシェルプロセスによって実行される可能性があります。
これはスクリプトでは機能しますが、ジョブ制御が有効な対話型シェルでは機能しません(ジョブ制御のために機能せず、対話型ではありません)。
例:
$ cat script.sh
cat << EOF > myfile
line1
line2
EOF
cat myfile | { read firstline; read secondline; }
printf 'first=%s\n' "$firstline"
printf 'second=%s\n' "$secondline"
shopt -s lastpipe
cat myfile | { read firstline; read secondline; }
printf 'first=%s\n' "$firstline"
printf 'second=%s\n' "$secondline"
$ bash script.sh
first=
second=
first=line1
second=line2
場合によっては、POSIXで完全にキャンセルしてパイプsh
するのではなく、両方の呼び出しを直接使用して複合コマンドにリダイレクトすることもできます。bash
cat
read
{ read firstline; read secondline; } <myfile
ちなみに、お使いのコンピュータには実際の歴史的なBourneシェルがない可能性があります(Solaris 11以前のSolarisシステムではない限り)。私はあなたが最新のPOSIXsh
シェルに言及していると仮定します。
答え2
パイプの右側を使用する場合は、いくつかの制限があります。
使用前に変数が設定されていないことを確認するために、コードを変更して少し圧縮しました。
#! /bin/sh
unset firstline secondline
printf '%s\n%s\n' line1 line2 > myfile
cat myfile | { read firstline; read secondline
echo "internal 1: $firstline" # "line1" in most shells
echo "internal 2: $secondline" # "line2" in most shells
}
echo "Outside 1: $firstline" # "line1" in ksh, zsh (even called as sh).
# Empty in sh, bash, mksh, dash, ash, yash.
echo "Outside 2: $secondline" # "line2" in ksh, zsh.
# Same as above for other shells.
複合コマンドの内部部分には{...}
値があり、外部部分(Outside
)は空であることが多いです(特にPOSIX、移植可能な場合)。
shopt -s lastpipe
Bashは、パイプシンボルの右側に値を設定した場合にのみ値を保持します。 ksh(93)とzshはどちらもデフォルトの読み取り値を保持します。
したがって、問題はパイプ|
表記を使用しないことです。まあ、この場合はとても簡単です。または、変数の値を移植可能に保つように設定しています<file { ... }
。{...} <file
#! /bin/sh
unset firstline secondline
printf '%s\n%s\n' line1 line2 > myfile
{ read firstline; read secondline
printf '%s\n' "internal 1: $firstline" # "line1" in most shells
printf '%s\n' "internal 2: $secondline" # "line2" in most shells
} < myfile
printf '%s\n' "Outside 1: $firstline" # "line1" in most shells.
printf '%s\n' "Outside 2: $secondline" # "line2" in most shells.
このワークセットは、以前のBourneシェルを除くすべてのシェルでテストされました(現在のシェルで実行するための{...}構成が不足しているため、解決策がより難しくなり、時間がかかることはありません。@schilyの回答を読んでください) 。
答え3
read
Bourne Shell
次の 2 つの制限により、Bourne Shell ではこの操作を使用できません。
パイプの右側は常にサブシェルで実行されます。
使用すると、コマンド
echo foo | read VAR
はread
常にサブシェルで実行されます。Bourne Shellは、シェルの組み込みコマンドを含むコマンドのリストを
{ ...; }
サブシェルに入れます。だから{ read A; read B; } < file
サブシェルでも実行されます。
これを確認したい場合は、移植性の高いSolaris Bourne Shellバージョンでobosh
確認することをお勧めします。schilytools
望むより:http://sourceforge.net/projects/schilytools/files/
最新のシェル(ksh93
これが組み込みシェルの場合は、プロセス内のメインシェルパイプの一番右側にあるプログラムを実行して起動します。したがって、bosh
これはBourneシェルの最新バージョンを使用すると便利です()サブシェルも生成bosh
されません){ ...; }
理由で副作用がある可能性があります。
{ read A; read B; } < file
そしてbosh
以下はすべてのシェルで機能します。
exec 3<&0 # save standard input as fd 3
exec < file # use file as stdin
read A
read B
exec 0<&3 # restore standard input
exec 3<&- # close file descriptor 3