一部の人々はread
、行を読むコマンドであるという誤った概念を持っています。そうではありません。
read
(おそらくバックスラッシュに続く)行から単語を読み取ります。単語は$IFS
区切られ、バックスラッシュを使用して区切り文字をエスケープ(または行を継続)できます。
一般的な構文は次のとおりです。
read word1 word2... remaining_words
read
エスケープされていない改行文字(または入力の終わり)が見つかるまでstdinを一度に1バイトずつ読み取り、それを複雑な規則に従って分割し$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
。まだread remaining_words
です。バックスラッシュ処理は引き続き行われ、IFSの空白文字は最初と最後から削除されます。
この-r
オプションは、バックスラッシュ処理を削除します。したがって、上記の同じコマンド-r
は代わりに割り当てます
$a
⇐ foo
$b
⇐ bar\
$c
⇐ baz bl\ah blah\
さて、分割部分については$IFS
、IFSの空白文字(つまりスペースとタブ(および改行、ただし、-dを使用しない限り重要ではありませんが))の2つのクラスの文字があることを認識することが重要です。$IFS
)およびその他のデフォルト値になります。これら2つのクラスのキャラクターの扱いは異なります。
IFS=:
(:
IFS空白文字でないという)、のような入力が:foo::bar::
に分割されるだろう""
、"foo"
、""
、bar
および""
(と余分な""
ものは除い重要ではありませんが、いくつかの実装でread -a
)。一方、それ:
をスペースで置き換えると、分割はとのみにfoo
なりbar
ます。つまり、先頭と末尾のものは無視され、それらのシーケンスは1つのように扱われます。で空白文字と非空白文字を組み合わせる場合、追加の規則があります$IFS
。一部の実装では、IFS(IFS=::
またはIFS=' '
)の文字を2倍にすることで、特別な処理を追加/削除できます。
したがって、ここで、先頭および末尾のエスケープされていない空白文字を削除したくない場合は、IFSからそれらのIFS空白文字を削除する必要があります。
IFSの非空白文字であっても、入力行にこれらの文字が1つ(1つだけ)含まれ、POSIXシェル(一部のバージョンでもない)の行の最後の文字(などIFS=: read -r word
の入力の場合foo:
)である場合、その入力一つとして考えられているものをシェルに、文字があるため単語として考えられているターミネータ、そう含まれています、ではありません。zsh
pdksh
foo
$IFS
word
foo
foo:
したがって、read
組み込みで1行の入力を読み取る標準的な方法は次のとおりです。
IFS= read -r line
(read
NUL文字はを除いてサポートされていないため、ほとんどの実装ではテキスト行でのみ機能することに注意してくださいzsh
)。
var=value cmd
構文を使用するとIFS
、そのcmd
コマンドの実行中にのみ異なる方法で設定されます。
履歴メモ
read
組み込みは、Bourneシェルによって導入し、読むためにすでにあった言葉ではなく、ラインを。最近のPOSIXシェルにはいくつかの重要な違いがあります。
Bourneシェルread
は-r
(Kornシェルによって導入された)オプションをサポートしていなかったため、そのようなもので入力を前処理する以外にバックスラッシュ処理を無効にする方法はありませんsed 's/\\/&&/g'
。
Bourneシェルには、2つのクラスの文字という概念がありませんでした(これもkshによって導入されました)。ボーンのすべての文字をシェルのIFS空白文字がkshの中でやるのと同じ治療を受け、それがあるIFS=: read a b c
ように、入力にfoo::bar
割り当てるでしょうbar
に$b
、空の文字列ではありません、。
Bourneシェルで:
var=value cmd
cmd
が(のようなread
)組み込みである場合、終了後にvar
設定されvalue
たままcmd
になります。$IFS
Bourneシェルで$IFS
は、展開だけでなくすべてを分割するために使用されるため、これは特に重要です。また、$IFS
Bourneシェルでスペース文字を削除すると、"$@"
機能しなくなります。
Bourneシェルでは、複合コマンドをリダイレクトすると、サブシェルで実行されます(初期バージョンでは、機能しなかっread var < file
たり機能exec 3< file; read var <&3
しなかったりします)。そのため、Bourneシェルread
では、端末上のユーザー入力以外に使用することはまれでした。(その行の継続処理が意味をなす場所)
一部のUnices(HP / UXなどutil-linux
)には、line
1行の入力を読み取るコマンドがあります(以前は、Single UNIX Specificationバージョン2までは標準のUNIXコマンドでした)。
これは基本的に同じですが、head -n 1
一度に1バイトずつ読み取り、複数行を読み取らないようにします。これらのシステムでは、次のことができます。
line=`line`
もちろん、それは新しいプロセスを生成し、コマンドを実行し、パイプを介してその出力を読み取ることを意味するため、kshの場合よりもはるかに効率が低くなりますIFS= read -r line
が、それでもはるかに直感的です。