在bash printf中进行变量替换 {}

3
我正在尝试使用变量打印10次true,但不起作用。
count=10
printf 'true\n%.0s' {1..$count}

这个是有效的:

printf 'true\n%.0s' {1..10}

我理解{}在变量之前被评估,但是我无法解决它。


请参见http://mywiki.wooledge.org/BashPitfalls#for_i_in_.7B1...24n.7D。 - Charles Duffy
4个回答

13

这不是关于 printf 的问题,而是关于 {1..$count} 的问题。这种扩展只能在常量中进行。

for ((i=1; i<=10; i++)); do
  printf 'true\n%.0s' "$i"
done

...或者,如果您真的想要扩展到单个命令行,请先将参数收集到一个数组中:

arr=()
for ((i=1; i<=10; i++)); do arr+=( "$i" ); done
printf 'true\n%.0s' "${arr[@]}"

为什么会这样呢:花括号扩展({1..10})发生在参数扩展($count)之。因此,当$count被扩展为10时,就不会再进行花括号扩展了。


这绝对是安全的方法+1 - anubhava
抱歉,我可能没有表达清楚。我知道使用for循环的解决方法,但是我正在寻找一种避免使用它的方法,比如使用eval。 - user3348409
3
不要为了简洁而牺牲安全性。使用循环。 - chepner

4
另一种方法(使用外部进程):
printf 'true\n%.0s' $(seq $count)

1
seq既不是POSIX规定的,也不是bash内置的命令,在支持bash的所有操作系统上都不可用。话虽如此,与使用“eval”相比,吃掉子进程的开销并承受可移植性损失要好得多,因此这是对已接受答案的改进; +1。 - Charles Duffy

1
“为了好玩,这里有一个稍微奇怪的方式:”
mapfile -n $count a < /dev/urandom; printf 'true\n%.0s' ${!a[@]}

踩的人总是会踩,我想。 - Digital Trauma
1
哦——实际上,由于你正在迭代索引而不是值,所以内容真的无关紧要,因此没有与读取内容相关的错误的可能性。我不能解释那个踩票,显然有人没有幽默感。 - Charles Duffy

-2

1
这里没有使用子shell的理由,它只会增加低效。如果你想使用eval(并查看@anubhava回答中关于安全影响的讨论),最好在当前shell中执行。 - Charles Duffy
2
引用该讨论中的一条评论,以防答案被撤回而消失:“如果有人在扩展"$filename"的代码周围添加了evalbash -c,当他们得到一个名为/tmp/$(rm -rf /)/hello的文件时,我不想负责任。” - Charles Duffy
太棒了!!这个可行,正是我想要的。 - user3348409

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接