「IFS=read-r-line」を理解する

「IFS=read-r-line」を理解する

内部フィールド区切り変数に値を追加することが可能であることは明らかです。たとえば、

$ IFS=blah
$ echo "$IFS"
blah
$ 

read -r lineまた、これがデータをstdin次の変数に保存することも学びましたline

$ read -r line <<< blah
$ echo "$line"
blah
$ 

しかし、コマンドはどのように変数に値を割り当てますか?まずstdin、to変数のデータを保存しlineてからlinetoの値を指定しますかIFS

ベストアンサー1

POSIX シェルではreadオプションがないと読み取れません。ワイヤー、それを読みます性格単語が区切られた(バックスラッシュで続く)行では、$IFSバックスラッシュを使用して区切り記号(または連続行)をエスケープできます。

一般的な構文は次のとおりです。

read word1 word2... remaining_words

readエスケープされていない改行文字(または入力の終わり)が見つかるまで一度に1バイトずつstdinを読み取り、複雑な規則に従って分割し、分割結果を$word1$word2...に保存します。$remaining_words

たとえば、次のような入力の場合:

  <tab> foo bar\ baz   bl\ah   blah\
whatever whatever

デフォルト値を使用すると、次のものが$IFS割り当てread a b cられます。

  • $afoo
  • $bbar baz
  • $cblah blahwhatever whatever

1つのパラメータだけを渡してもread lineStillにはなりませんread remaining_words。バックスラッシュ処理は引き続き実行され、IFS スペース文字² はまだ最初と最後から削除されます。

この-rオプションはバックスラッシュ処理を削除します。したがって、上記の同じコマンドが-r代わりに割り当てられます。

  • $afoo
  • $bbar\
  • $cbaz bl\ah blah\

さて、分割部分の場合、2つのカテゴリの文字があることを認識することが重要です$IFS$IFS)などのデフォルト値で発生します。これら2つのタイプの役割は異なる方法で扱われます。

IFS=::IFS以外)空白文字の場合、このような入力は、および(およびいくつかの実装追加項目、それ以上は重要ではありません):foo::bar::に分割されます。そして、スペースに置き換えると、合計にのみ分割されます。言い換えれば、先行および後続のシーケンスは無視され、対応するシーケンスは1つのシーケンスとして扱われる。で空白と空白以外の文字を結合するときに追加の規則があります。一部の実装では、IFSの文字(または)を2倍に増やして特別な処理を追加/削除することができます。"""foo"""bar""""read -a:foobar$IFSIFS=::IFS=' '

したがって、ここでエスケープされていない先行および末尾の空白文字を削除したくない場合は、IFSからそのIFS空白文字を削除する必要があります。

空白以外のIFS文字がある場合でも、入力行にこれらの文字の1つだけが含まれており、IFS=: read -r wordPOSIXシェル(一部のバージョンではない)の行の最後の文字(同様の入力と同様)の場合、入力は単語として扱われます。なぜなら、これらのシェルでは、文字は次のように処理されるからです。foo:zshpdkshfoo$IFSターミネーターしたがって、はword含まれてfooいませんfoo:

したがって、組み込み関数を使用して入力行を読み取る標準的な方法は次のとおりreadです。

IFS= read -r line

(ほとんどのread実装ではNUL文字はサポートされていないため、これはテキスト行にのみ機能しますzsh。)

コマンドの実行中にのみ他の設定が行われるようにするには、var=value cmd構文を使用します。IFScmd

歴史的記録

このread組み込み関数は Bourne シェルによって導入され、すでに読み取ることができます。性格、行ではありません。最新のPOSIXシェルにはいくつかの重要な違いがあります。

Bourneシェルはオプション(Kornシェルによって導入された)をreadサポートしていないため、-r同様の方法で入力を前処理する以外にバックスラッシュ処理を無効にする方法はありませんsed 's/\\/&&/g'

Bourneシェルには2種類の文字概念はありません(これはkshでも導入されました)。 Bourneシェルでは、すべての文字は空の文字列ではなくksh、つまりIFS=: read a b c入力(foo::barに割り当てられている場合bar)のIFS空白文字と同じように扱われます。$b

Bourne シェルでは、以下を使用します。

var=value cmd

cmd組み込みの場合(例:)read完了後もvar設定されたままです。 Bourneシェルでは、拡張だけでなくすべてを分割するために使用されるため、これは特に重要です。また、Bourne シェルから空白文字を削除しても機能しなくなります。valuecmd$IFS$IFS$IFS"$@"

Bourneシェルから複合コマンドをリダイレクトすると、そのコマンドはサブシェルで実行されるため(初期バージョンでは同様でも機能しなくてもread var < fileexec 3< file; read var <&3、Bourneシェルはread端末のユーザー入力以外の目的でほとんど使用されません。 (ライン連続処理が意味がある場合)

一部のUnices(HP / UXなどutil-linux)にはまだline入力行を読み取るコマンドがあります(これは以前は標準のUNIXコマンドでした)。シングルUNIX仕様バージョン2)。

head -n 1これは、1行以上読み取らないように一度に1バイトずつ読み込むことを除いて、基本的に同じです。これらのシステムでは、次のことができます。

line=`line`

もちろん、これは新しいプロセスを生成し、コマンドを実行し、パイプを介して出力を読み取ることを意味するので、kshよりはるかに効率的ではありませんが、それでもはるかにIFS= read -r line直感的です。


1 検索可能な入力ですが、一部の実装ではブロック読み取りに戻り、最適化に逆追跡することができます。 ksh93はさらに一歩進んで読んだことを思い出し、次のread呼び出しに使用します。現在破損している

²IFS スペース文字、各POSIXはロケールに分類された文字であり、ksh88(POSIX仕様のベース)とほとんどのシェルで発生する[:space:]ように、これはまだSPC、TAB、およびNLに制限されています。$IFSこれに関して私が見つけた唯一のPOSIX互換シェルはですyashksh93bash5.0ベース)他のスペース(CR、FF、VTなど)も含まれていますが、シングルバイトスペースに制限されています(たとえば、Solaris)(一部の領域にはシングルバイトの切り捨て防止スペースが含まれています)

おすすめ記事