printfが予想より多くの引数を出力するのはなぜですか?


9

このシェルスクリプトが入力を2回出力するのはなぜですか?

スクリプトが5以降の入力を無視することを期待していました。

脚本:

#! /bin/bash
echo "Enter 5 words : "
read  a b c d e 
printf "> %s %s %s %s %s <" $a $b $c $d $e

出力:

user@linux:~$ pico ifs2.sh
user@linux:~$ ./ifs2.sh
Enter 5 words : 
1 2 3 4 5 
> 1 2 3 4 5 <user@linux:~$ ./ifs2.sh
Enter 5 words : 
1 2 3 4 5 6
> 1 2 3 4 5 <> 6     <user@linux:~$ ./ifs2.sh
Enter 5 words : 
1 2 3 4 5 6 7 8 9 0
> 1 2 3 4 5 <> 6 7 8 9 0 <user@linux:~$ 

また、次のスクリプトは、$ IFSに何が設定されていても機能します。どうして?

#! /bin/bash    
old="$IFS"
IFS=":"
echo "IFS = $IFS"
echo "Enter 5 words : "
read  a b c d e 
printf "> %s %s %s %s %s <" $a $b $c $d $e    
IFS="$old"

出力:

user@linux:~$ ./ifs2.sh
IFS = :
Enter 5 words : 
1 2 3 4 5  
> 1 2 3 4 5      <user@linux:~$ ./ifs2.sh
IFS = :
Enter 5 words : 
1 2 3 4 5
> 1 2 3 4 5     <user@linux:~$ ./ifs2.sh
IFS = :
Enter 5 words : 
1:2:3:4:5
> 1 2 3 4 5 <user@linux:~$ 

書式指定子に関連付けられprintf\cエスケープでいつでも停止し%bます。例:printf %s%\ d%b thing 3 "${var+\cquit printing if set}\nelse do a newline" and 0 keep\ going.
mikeserv 2015年

回答:


18

次の3つの問題があります。

  1. を使用するreadと、入力のフィールドよりも変数名の数が少ない場合、最後のvarは行の残りのすべてのフィールドに区切り文字でバインドされます。それはあなたの最初の予期しない例に$e入るということ5 6です。
  2. すべての$a.. $eは引用符で囲まれていないため、それらの値はフィールド分割されます。$e5 6」を保持している場合は、コマンドの2つの引数に展開されます。
  3. printf%置換の数と同じ数の引数を一度に繰り返し使用して、すべての引数を消費します。これは次のようにドキュメントに埋め込まれています

    formatオペランドは、引数のオペランドを満足するよう、多くの場合、必要に応じて再利用されなければなりません。追加cまたはs変換指定子は、null文字列引数が指定されたかのように評価されます。他の追加の変換指定は、引数がゼロの場合と同様に評価されます。

    つまり、未使用の引数がある場合は、最初からやり直し、フォーマット文字列全体を含めて、最初から処理します。これは、配列全体をフォーマットする場合に便利です。たとえば、次のようにします。

    printf '%b ' "${array[@]}"

    あなたのprintfコマンドは、のそれぞれから一つの引数を取得します$a。.. $dして、しかし多くはから残っています$e$eが " 5 6"の場合、printf2つの方法があります。2つ目は6フォーマットを開始します。その場合5 6 7 8 9 10は、2番目の印刷の代替品がすべて用意されています。


これらすべてを回避するには、ダミーフィールドをreadに追加し、パラメーターの置換を引用します(これは常に良いアイデアです)。

read  a b c d e dummy
printf "> %s %s %s %s %s <" "$a" "$b" "$c" "$d" "$e"

これは与えるでしょう:

Enter 5 words : 
1 2 3 4 5 6 7 8 9 10
> 1 2 3 4 5 <

dummy追加のフィールドをすべて取得し、printf期待した5つの引数のみを取得します。


2番目の編集済みの質問にも同様の答えがあります。スペースがないa場合にのみ値を取得しますIFS。つまり、$b.. $eは何も展開せず、printf単一の引数しか取得しません。書式文字列のスペースが出力され、その間に何も置換されません( "null文字列引数が指定されたかのように")。


"$ a" ..... "$ e"を使用して2番目のスクリプトを再度テストしました。2番目のスクリプトは、同じ問題を再度与えています。

3
引用によって2番目のスクリプトが変わることはありません。単一の文字列としてa1 2 3 4 5を持ち、一度にすべて置換されます。
Michael Homer

6
 printf "> %s < " 1 2 3

印刷します

 > 1 <> 2 <> 3 <

  printf "> %s %s <" 1 2 3

プリント

 > 1 2 <> 3  <

printf 書式文字列を満たすためにすべての引数を消費し、すべての引数が処理されるまで繰り返します。

2番目のスクリプトが機能するの$aは、が割り当てられるだけなので、コマンドが追加の反復にオーバーフローしないためです(反復は1つしかない)。


この動作は、で提供されるテキストに記載されていますhelp printf

...この形式は、すべての引数を消費するために必要に応じて再利用されます。形式が必要とするよりも引数が少ない場合、追加の形式指定は、ゼロ値またはnull文字列が適切に提供されたかのように動作します。...

およびhttp://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.htmlによって義務付けられています


これはなぜですか?文書化されていますか?
Shiplu Mokaddim、2015年

1
@Shipluは、動作が文書化されている場所と、動作を必要とする標準に関する段落を追加しました。
PSkocik
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.