echoとprintfだけではありません
まず、read a b c
部品で何が起こるかを理解しましょう。space-tab-newline read
であるIFS
変数のデフォルト値に基づいて単語分割を実行し、それに基づいてすべてに適合します。保持する変数よりも多くの入力がある場合、分割された部分が最初の変数に適合し、適合できないものは最後になります。ここに私が意味するものがあります:
bash-4.3$ read a b c <<< "one two three four"
bash-4.3$ echo $a
one
bash-4.3$ echo $b
two
bash-4.3$ echo $c
three four
これはまさにbash
のマニュアルで説明されている方法です(回答の最後の引用を参照)。
あなたの場合、1と2がaとbの変数に適合し、cは他のすべてを取ります3 4 5 6
。
また、多くの場合に表示されるのは、人々while IFS= read -r line; do ... ; done < input.txt
がテキストファイルを1行ずつ読み取るために使用することです。繰り返しますが、IFS=
ここにあるのは、単語の分割を制御する理由、またはより具体的には-無効にして、1行のテキストを変数に読み込むためです。存在しない場合、read
個々の単語をline
変数に適合させようとします。しかし、これは別の話ですwhile IFS= read -r variable
。非常に頻繁に使用される構造なので、後で勉強することをお勧めします。
echo vs printfの動作
echo
ここで期待することを行います。変数read
を配置したとおりに表示します。これは、以前の議論ですでに実証されています。
printf
変数がすべて使い果たされるまで変数をフォーマット文字列に適合させ続けるため、非常に特殊です。したがって、printf "%d, %d, %d \n" $a $b $c
printf を実行すると、小数点以下3桁の書式文字列が表示されますが、3つ以上の引数があります(変数は実際には個々の1,2,3,4,5,6に展開されるため)。これは紛らわしいように聞こえるかもしれませんが、実際の printf()
関数がC言語で行うことから改善された動作としての理由で存在します。
ここで出力に影響することは、変数が引用されていないことです。これにより、シェル(ではなくprintf
)が変数を6つの個別の項目に分解できます。これを引用と比較してください:
bash-4.3$ read a b c <<< "1 2 3 4"
bash-4.3$ printf "%d %d %d\n" "$a" "$b" "$c"
bash: printf: 3 4: invalid number
1 2 3
$c
変数が引用符で囲まれているため、1つの文字列として認識される3 4
ようになり、%d
フォーマットに適合しません。これは単一の整数です
引用せずに同じことを行います:
bash-4.3$ printf "%d %d %d\n" $a $b $c
1 2 3
4 0 0
printf
繰り返しますが、「OK、そこには6つのアイテムがありますが、フォーマットは3つしか表示されないので、ユーザーからの実際の入力と一致しないものは何でも合わせて空白のままにします」。
そして、これらのすべての場合において、あなたはそれについて私の言葉をとる必要はありません。実行strace -e trace=execve
して、コマンドが実際に「見る」ものを自分で確認してください。
bash-4.3$ strace -e trace=execve printf "%d %d %d\n" $a $b $c
execve("/usr/bin/printf", ["printf", "%d %d %d\\n", "1", "2", "3", "4"], [/* 80 vars */]) = 0
1 2 3
4 0 0
+++ exited with 0 +++
bash-4.3$ strace -e trace=execve printf "%d %d %d\n" "$a" "$b" "$c"
execve("/usr/bin/printf", ["printf", "%d %d %d\\n", "1", "2", "3 4"], [/* 80 vars */]) = 0
1 2 printf: ‘3 4’: value not completely converted
3
+++ exited with 1 +++
その他の注意事項
Charles Duffyがコメントで適切に指摘したように、コマンドで使用しbash
ている独自のビルトインがありprintf
、strace
実際/usr/bin/printf
にはシェルのバージョンではなくバージョンを呼び出します。わずかな違いは別として、この特定の質問に関心があるため、標準形式指定子は同じであり、動作も同じです。
また、覚えておくべきことは、printf
構文はecho
C よりもはるかに移植性があり(したがって、好ましい)ことです。もちろん、構文はCまたはその中にprintf()
機能を持つC言語に馴染みがあることは言うまでもありません。vs のテーマに関するterdonのこの優れた回答を参照してください。Ubuntuの特定のバージョンの特定のシェルに合わせて出力を作成することもできますが、異なるシステム間でスクリプトを移植する場合は、おそらくエコーよりも好むはずです。たぶんあなたは、UbuntuやCentOSのマシンを使って作業する初心者のシステム管理者かもしれませんし、FreeBSDさえ知っているかもしれませんので、そのような場合には選択をしなければなりません。printf
echo
printf
bashマニュアル、シェルビルチンコマンドセクションからの引用
読み取り[-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ... ]
標準入力または-uオプションの引数として指定されたファイル記述子fdから1行が読み取られ、最初の単語が名に、2番目の単語が2番目の名前に、というように残りが割り当てられます苗字に割り当てられた単語とその間にある区切り文字。入力ストリームから読み取られる単語が名前よりも少ない場合、残りの名前には空の値が割り当てられます。IFSの文字は、シェルが拡張に使用するのと同じルールを使用して行を単語に分割するために使用されます(上記の「単語の分割」で説明)。
strace
ケースと他のケースの注目すべき違いの1つstrace printf
は、を使用/usr/bin/printf
しているのに対しprintf
、bash では直接同じ名前のシェル組み込みを使用していることです。それらは常に同一ではありません-たとえば、bashインスタンスにはフォーマット指定子が%q
あり、新しいバージョンで$()T
は時間フォーマット用です。