为什么这个 shell 脚本打印输入两次?
我预计脚本会忽略 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:~$
答案1
你有三个问题:
- 和
read
,如果变量名少于输入中的字段,则最后一个 var 将使用分隔符绑定到该行中的所有剩余字段。这意味着这$e
会出现5 6
在你的第一个意想不到的例子中。 - 因为所有
$a
..$e
都没有被引用,所以它们的值会发生变化场分裂。如果$e
持有“5 6
”则它扩展为二命令的参数。 printf
消耗其所有参数,一次使用与%
替换一样多的参数,重复使用。这是埋藏在文档中作为:format
应根据需要多次重用操作数以满足参数操作数。任何额外c
或s
转换说明符都应像提供空字符串参数一样进行评估;其他额外的转换规范应像提供零参数一样进行评估。换句话说,如果有未使用的参数,它会重新开始并从头开始处理它们,包括整个格式字符串。当您想要格式化整个数组时,这非常有用,例如:
printf '%b ' "${array[@]}"
您的
printf
命令从每个$a
..获取一个参数$d
,然后从 .. 中剩下许多参数$e
。当$e
是“ ”时5 6
,printf
有两次循环,第二次刚刚开始6
格式化。当它出现时,5 6 7 8 9 10
它具有第二次印刷的全部替代品。
您可以通过向 中添加额外的虚拟字段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
仅获取您期望的五个参数。
您的第二个编辑问题有类似的答案:仅在没有空格a
时才获取值。IFS
这意味着$b
..$e
扩展到什么都没有,所以printf
只得到一个参数。打印格式字符串中的空格,它们之间没有任何替换(“就像提供了空字符串参数一样”)。
答案2
printf "> %s < " 1 2 3
将打印
> 1 <> 2 <> 3 <
printf "> %s %s <" 1 2 3
印刷
> 1 2 <> 3 <
printf
吃掉所有参数以满足其格式字符串,然后重复直到处理完所有参数。
第二个脚本之所以有效,是因为 only$a
曾被分配给,因此该命令不会溢出到其他迭代中(只有一次迭代)。
此行为记录在提供的文本中help printf
:
...该格式根据需要重新使用以消耗所有参数。如果参数少于格式所需的数量,则额外的格式规范的行为就像已提供适当的零值或空字符串一样。 ...
并由以下机构授权http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html