遍历 $@ 的值

Iterating over the values of $@

我想遍历给 bash 脚本的参数,例如:

./bash_script file1 file2 file3

$@ 为我提供了所有提供给脚本的文件,但我如何遍历这些文件?

我想 cat 每个文件并使用 awk 删除内容(我知道该怎么做,正是 $@ 的解包让我感到困惑)。

诀窍是 双引号 它就像 "$@".

foo(){
   printf ' ->%s\n' "$@";
}
foo "a b" c "d e"

相当于:

printf ' ->%s\n' "a b" c "d e"

如果上面的$@不是双引号,那么你会得到:

 printf ' ->%s\n' a b c d e

由于 $IFS 个字符的分词($IFS 默认为 ' '$'\t'$'\n',即一个 space、一个制表符和一个换行符)


$@ 对比 $*

对于任何 array:

上的任何 @-expansion 双引号都是这样工作的
$ foo=( "a b" c "d e" )
$ printf ' ->%s\n' "${foo[@]}"
   ->a b
   ->c
   ->d e

相比之下,*-expansions(例如,$*${foo[*]})将使用 $IFS 的第一个字符加入将数组的项目合并为一个字符串:

$ foo=( "a b" c "d e" )
$ ( IFS=:; printf ' ->%s\n' "${foo[*]}" )
 ->a b:c:d e

如果不加引号,它将再次拆分这个 IFS 字符:

$ foo=( "a b" c "d e:f:g" )
$ ( IFS=:; printf ' ->%s\n' ${foo[*]} ) 
 ->a b
 ->c
 ->d e
 ->f
 ->g

在 for 循环中迭代 $@ 的技巧:

"$@"数组比较特殊。如果你想在 for 循环中遍历 "$@",那么你可以缩写

 for variable_name in "$@"; do
   ...
 done

作为

 for variable_name; do 
 done

因为跳过 for 循环的 in something 部分意味着 in "$@".

这甚至在 POSIX-only shells 中也有效(dash,bourne shell)没有数组变量但支持 "$@""$*.

要遍历它们,您可以使用双引号 $@,如下所示:

for arg in "$@" ; do
    echo "--> ${arg}"
done

for arg in "$@" 也可以写成 for arg(甚至 for arg in),因为 bash 手册页指出:

for name [ [ in [ word ... ] ] ; ] do list ; done

blah blah blah

If the in word is omitted, the for command executes list once for each positional parameter that is set.

不过,我更喜欢显式变体。

以下文字记录显示了这一点:

pax: testprog.sh 1 2 3 '4 5' '' '6 7 8' "9"
--> 1
--> 2
--> 3
--> 4 5
--> 
--> 6 7 8
--> 9

顺便说一句,您关于使用 catawk 修改文件的评论可能会平息“无用地使用 cat 奖励”人群的愤怒:-)

如果您正在考虑类似的事情:

cat "${fspec}" | awk 'do something'

那么cat就完全没有必要了。您可以改用:

awk 'do something' "${fspec}"

我通常不担心 运行 额外流程的(小的)低效问题,但有些人会担心。