内部フィールドセパレータ変数に値を追加できることは明らかです。例:
$ IFS=blah
$ echo "$IFS"
blah
$
read -r line
も理解しています stdin
からデータを保存します line
という名前の変数に :
$ read -r line <<< blah
$ echo "$line"
blah
$
しかし、コマンドはどのようにして変数値を割り当てることができますか?そして、最初に stdin
からのデータを保存しますか 変数line
次に、 line
の値を指定します IFS
へ ?
承認された回答:
POSIXシェルでは、 read
、オプションがないと行は読み取れません 、単語を読み取ります 単語が$IFS
である(おそらくバックスラッシュ-続きの)行から 区切り文字と円記号を使用して、区切り文字をエスケープする(または行を続ける)ことができます。
一般的な構文は次のとおりです。
read word1 word2... remaining_words
読むコード> エスケープされていない改行文字(または入力の終わり)が見つかるまで一度に1バイトずつstdinを読み取り¹、複雑なルールに従ってそれを分割し、その分割の結果を
$ word1
に格納します。 、 $ word2
…$initial_words
。
たとえば、次のような入力で:
<tab> foo bar baz blah blah
whatever whatever
デフォルト値は$IFS
、 read a b c
割り当てる:
-
$ a
⇐foo
-
$ b
⇐barbaz
-
$ c
⇐何とか何でも
これで、引数を1つだけ渡した場合、それは read line
にはなりません。 。まだreadremaining_words
。バックスラッシュ処理は引き続き実行され、IFS空白文字²は最初と最後から削除されます。
-r
オプションはバックスラッシュ処理を削除します。したがって、上記の -r
を使用した同じコマンド 代わりに割り当てます
-
$ a
⇐foo
-
$ b
⇐バーコード>
-
$ c
⇐bazblah blah
ここで、分割部分については、 $ IFS
には2つのクラスの文字があることを理解することが重要です。 :IFS空白文字²(スペースとタブ(および改行を含みますが、ここでは-dを使用しない限り重要ではありません)。これもデフォルト値の $ IFS
にあります。 )およびその他。これら2つのクラスのキャラクターの扱いは異なります。
IFS =:
を使用 (:コード> IFS空白文字ではない)、
:foo ::bar ::
のような入力 ""
に分割されます 、 "foo"
、 ""
、バーコード> および
""
(および追加の ""
一部の実装では、 read -a
以外は問題ありません。 )。そのを置き換えると:
スペースを使用すると、分割は foo
のみに行われます。 およびbar
。つまり、先頭と末尾のものは無視され、それらのシーケンスは1つのように扱われます。 $ IFS
で空白文字と非空白文字を組み合わせる場合は、追加の規則があります。 。一部の実装では、IFSの文字を2倍にすることで、特別な処理を追加/削除できます( IFS =::
またはIFS=''
。
したがって、ここで、先頭と末尾のエスケープされていない空白文字を削除したくない場合は、それらのIFS空白文字をIFSから削除する必要があります。
IFS以外の空白文字を使用している場合でも、入力行にそれらの文字が1つ(そして1つだけ)含まれていて、それが行の最後の文字である場合( IFS =:read -r word
など) foo:
のような入力で )POSIXシェル( zsh
ではない) また、一部の pdksh
バージョン)、その入力は1つの foo
と見なされます これらのシェルでは、文字 $ IFS
ターミネータと見なされます 、つまり word
foo
が含まれます 、 foo:
ではありません 。
したがって、 read
を使用して1行の入力を読み取る標準的な方法 組み込みは:
IFS= read -r line
(ほとんどの read
に注意してください zsh
を除いて、NUL文字はサポートされていないため、テキスト行に対してのみ機能する実装 。
var =value cmd
を使用する 構文により、 IFS
が確認されます そのcmd
の期間中のみ異なる設定になります コマンド。
履歴メモ
read
ビルトインはBourneシェルによって導入され、すでに単語を読んでいました。 、行ではありません。最新のPOSIXシェルにはいくつかの重要な違いがあります。
Bourneシェルのread
-r
をサポートしていません オプション(Kornシェルによって導入された)。したがって、 sed's / \ / &&/ g'
のようなもので入力を前処理する以外に、バックスラッシュ処理を無効にする方法はありません。 そこに。
Bourneシェルには、2つのクラスの文字の概念がありませんでした(これもkshによって導入されました)。 Bourneシェルでは、すべての文字がkshでのIFS空白文字と同じ扱いを受けます。つまり、 IFS =:read a b c
foo ::bar
のような入力で bar
を割り当てます $ b
へ 、空の文字列ではありません。
Bourneシェルでは、次のようになります:
var=value cmd
cmd
の場合 組み込みです( read
など) is)、 var
value
に設定されたまま cmd
の後 終わりました。これは、 $ IFS
では特に重要です Bourneシェルでは、 $ IFS
拡張だけでなく、すべてを分割するために使用されます。また、 $ IFS
からスペース文字を削除した場合 Bourneシェルで、 "[email protected]"
動作しなくなりました。
Bourneシェルでは、複合コマンドをリダイレクトすると、サブシェルで実行されます(初期のバージョンでは、 read var
exec3<ファイル; var <&3
を読む 動作しませんでした)、そのため、Bourneシェルで read
を使用することはめったにありませんでした 端末でのユーザー入力以外の場合(その行継続処理が理にかなっている場合)
一部のユニス(HP / UXなど、 util-linux
にも1つあります) )まだ行
があります 1行の入力を読み取るコマンド(Single UNIX Specificationバージョン2までは標準のUNIXコマンドでした)。
これは基本的にhead-n 1
と同じです ただし、一度に1バイトずつ読み取って、複数行を読み取らないようにします。これらのシステムでは、次のことができます。
line=`line`
もちろん、これは新しいプロセスを生成し、コマンドを実行し、パイプを介してその出力を読み取ることを意味するため、kshの IFS =read -r line
よりもはるかに効率が低くなります。 、しかしそれでもはるかに直感的です。