I am trying to run multiple similar commands using a for loop and string formatting.
I am able to create and then print the strings of the commands I would like to run:
$foo=( "bar" "baz" )
$for ((j=0;j<2;++j)); do printf "touch %s_file.txt; " "${foo[j]}"; done
touch bar_file.txt; touch baz_file.txt;
But I want to enter these strings as a command. Based on other questions I was thinking that eval
would do what I need:
$for ((j=0;j<2;++j)); do eval "touch %s_file.txt; " "${foo[j]}"; done
-bash: bar: command not found
-bash: baz: command not found
I was expecting(or hoping) the output would be equivalent to the output of:
$touch bar_file.txt; touch baz_file.txt;
The touch
here is just an example. I'm more interested in how to format a string and then run it as a command. Thanks.
答案 0 :(得分:3)
首先,这里不需要使用字符串格式来生成代码,或者通常 - BashFAQ #50描述用例和更好的实践替代方案。这可以简单如下:
for j in in "${foo[@]}"; do touch "${j}_file.txt"; done
其次,如果你必须,那就这样做:
printf -v cmd 'touch %q_file.txt; ' "${foo[@]}"
eval "$cmd"
这将设置cmd='touch bar_file.txt; touch baz_file.txt '
,然后执行它。
当shell稍后解析内容时使用%q
(与eval
一样)确保您的数组元素的格式化能够在shell解析后生存 - 所以如果你有foo=( "name with spaces" 'name with $(rm -rf $HOME) dangerous content' )
,您将正确触摸'name with spaces_file.txt'
和'name with $(rm -rf $HOME) dangerous content_file.txt'
,而不是执行危险内容并根据空格触摸多个文件。
答案 1 :(得分:1)
Instead of trying to put the whole command in a string, why not make a function to do the thing you want like:
update_file() {
touch "$1"
}
foo=( "bar" "baz" )
for ((j=0;j<2;++j)); do
update_file "$(printf "%s_file.txt" "${foo[j]}")"
done
then you can make that function do arbitrarily complex things with the files
As a general rule, variables hold data, not code. You can sometimes make it work putting code into data, but that way lies dragons. See here for some good reading on that