内部フィールド区切り変数に値を追加することが可能であることは明らかです。たとえば、
$ IFS=blah
$ echo "$IFS"
blah
$
read -r line
また、これがデータをstdin
次の変数に保存することも学びましたline
。
$ read -r line <<< blah
$ echo "$line"
blah
$
しかし、コマンドはどのように変数に値を割り当てますか?まずstdin
、to変数のデータを保存しline
てからline
toの値を指定しますか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
られます。
$a
⇐foo
$b
⇐bar baz
$c
⇐blah blahwhatever whatever
1つのパラメータだけを渡してもread line
Stillにはなりませんread remaining_words
。バックスラッシュ処理は引き続き実行され、IFS スペース文字² はまだ最初と最後から削除されます。
この-r
オプションはバックスラッシュ処理を削除します。したがって、上記の同じコマンドが-r
代わりに割り当てられます。
$a
⇐foo
$b
⇐bar\
$c
⇐baz bl\ah blah\
さて、分割部分の場合、2つのカテゴリの文字があることを認識することが重要です$IFS
。$IFS
)などのデフォルト値で発生します。これら2つのタイプの役割は異なる方法で扱われます。
IFS=:
(:
IFS以外)空白文字の場合、このような入力は、および(およびいくつかの実装追加項目、それ以上は重要ではありません):foo::bar::
に分割されます。そして、スペースに置き換えると、合計にのみ分割されます。言い換えれば、先行および後続のシーケンスは無視され、対応するシーケンスは1つのシーケンスとして扱われる。で空白と空白以外の文字を結合するときに追加の規則があります。一部の実装では、IFSの文字(または)を2倍に増やして特別な処理を追加/削除することができます。""
"foo"
""
bar
""
""
read -a
:
foo
bar
$IFS
IFS=::
IFS=' '
したがって、ここでエスケープされていない先行および末尾の空白文字を削除したくない場合は、IFSからそのIFS空白文字を削除する必要があります。
空白以外のIFS文字がある場合でも、入力行にこれらの文字の1つだけが含まれており、IFS=: read -r word
POSIXシェル(一部のバージョンではない)の行の最後の文字(同様の入力と同様)の場合、入力は単語として扱われます。なぜなら、これらのシェルでは、文字は次のように処理されるからです。foo:
zsh
pdksh
foo
$IFS
ターミネーターしたがって、はword
含まれてfoo
いませんfoo:
。
したがって、組み込み関数を使用して入力行を読み取る標準的な方法は次のとおりread
です。
IFS= read -r line
(ほとんどのread
実装ではNUL文字はサポートされていないため、これはテキスト行にのみ機能しますzsh
。)
コマンドの実行中にのみ他の設定が行われるようにするには、var=value cmd
構文を使用します。IFS
cmd
歴史的記録
この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 シェルから空白文字を削除しても機能しなくなります。value
cmd
$IFS
$IFS
$IFS
"$@"
Bourneシェルから複合コマンドをリダイレクトすると、そのコマンドはサブシェルで実行されるため(初期バージョンでは同様でも機能しなくてもread var < file
)exec 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互換シェルはですyash
。ksh93
(bash
5.0ベース)他のスペース(CR、FF、VTなど)も含まれていますが、シングルバイトスペースに制限されています(たとえば、Solaris)(一部の領域にはシングルバイトの切り捨て防止スペースが含まれています)