bash特殊参数的问题

时间:2018-08-03 12:36:49

标签: bash

有人可以解释为什么此输出拆分到新行吗?

$ cat ./test-args.sh
#!/usr/bin/env bash
while [[ $# -gt 0 ]]; do
    printf '%b %b %b %b\n' "$0" "$1" "$#" "$@"
    shift
done

$ ./test-args.sh a b c d e
./test-args.sh a 5 a
b c d e
./test-args.sh b 4 b
c d e
./test-args.sh c 3 c
d e
./test-args.sh d 2 d
e
./test-args.sh e 1 e

将特殊参数分配给变量可以按预期工作:

$ cat ./test-args.sh
#!/usr/bin/env bash
while [[ $# -gt 0 ]]; do
    a=$0
    b=$1
    c=$#
    d=$@
    printf '%b %b %b %b\n' "$a" "$b" "$c" "$d"
    shift
done

$ ./test-args.sh a b c d e
./test-args.sh a 5 a b c d e
./test-args.sh b 4 b c d e
./test-args.sh c 3 c d e
./test-args.sh d 2 d e
./test-args.sh e 1 e

3 个答案:

答案 0 :(得分:4)

printf收到额外的参数时,它将在额外的单词上重复格式字符串。例如:

$ printf '[%s]\n' a b c
[a]
[b]
[c]

您的printf格式字符串需要四个参数,但是"$@"扩展为多个单词。它在格式字符串上运行了两次,这就是\n在“中间”显示的原因。如果将格式字符串更改为'<%b %b %b %b>\n',则会很清楚:

$ ./test-args.sh a b c d e
<./test-args.sh a 5 a>
<b c d e>
<./test-args.sh b 4 b>
<c d e >
<./test-args.sh c 3 c>
<d e  >
<./test-args.sh d 2 d>
<e   >
<./test-args.sh e 1 e>

您可以通过将"$@"更改为"$*"来解决此问题,以便所有参数都作为单个字符串传递。

为什么分配变量会改变行为?这是因为d=$@不能保留$@的“排列性”。像$*一样,它将参数连接成一个字符串。要获得数组行为,您需要编写:

a=$0
b=$1
c=$#
d=("$@")
printf '%b %b %b %b\n' "$a" "$b" "$c" "${d[@]}"

答案 1 :(得分:1)

如果为printf提供的属性超出其格式使用的属性,它将重新使用其格式。在您的情况下,"$@"扩展为用双引号引起来的所有参数,(在第一种情况下,"a" "b" "c" "d" "e"),并且printf收到8个参数,并使用两次出现的格式字符串来显示,提供您意想不到的输出。

为避免此行为,您将需要使用"$*",它将扩展为单个参数,在第一种情况下为"a b c d e"

答案 2 :(得分:0)

想法是,printf遍历参数。因此,在第一个循环中

printf '%b %b %b %b\n' "$0" "$1" "$#" "$@"

扩展到

printf '%b %b %b %b\n' ./test-args.sh a 5 a b c d e

printf接受前四个参数./test-args.sh a 5 a并使用%b %b %b %b\n打印它们。然后,它需要另外4个(或更少)自变量b c d e并再次使用%b %b %b %b\n来打印它们。基本上printf '%b %b %b %b\n' ./test-args.sh a 5 a b c d e

相同
printf '%b %b %b %b\n' ./test-args.sh a 5 a b
printf '%b %b %b %b\n' b c d e

因此它打印多行。
这将满足您的要求:

while (($#)); do
    printf '%s ' "$0" "$1" "$#" "$@"
    printf '\n'
    shift
done

或者也许:

while (($#)); do
    printf '%s %s %s %s\n' "$0" "$1" "$#" "$*"
    shift
done