Bourne Shellで複数行を読む

Bourne Shellで複数行を読む

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 つの変数firstlinesum secondline(両方を読み取った後) が含まれますが、サブシェルが終了すると、両方の変数が削除され、削除されます。

これはPOSIXshbash

(4.2+)では、bashシェルオプションを設定することでこの問題を解決できますlastpipebashマニュアルから:

パイプラインの各コマンドは、別々のプロセス(つまりサブシェルで)で実行されます。サブシェル環境の説明については、コマンド実行環境を参照してください。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するのではなく、両方の呼び出しを直接使用して複合コマンドにリダイレクトすることもできます。bashcatread

{ 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 lastpipeBashは、パイプシンボルの右側に値を設定した場合にのみ値を保持します。 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

readBourne Shell次の 2 つの制限により、Bourne Shell ではこの操作を使用できません。

  • パイプの右側は常にサブシェルで実行されます。

    使用すると、コマンドecho foo | read VARread常にサブシェルで実行されます。

  • 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

関連情報