参数名称内的 Bash 数组参数扩展:错误替换
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7142965/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Bash array parameter expansion within parameter name: bad substitution
提问by Aesthir
I wrote a script in which I want the user to input a directory or many directories and each is checked for something unimportant to this discussion. All interior directories are also checked up to a specified depth.
我编写了一个脚本,我希望用户在其中输入一个或多个目录,然后检查每个目录中是否存在对本次讨论不重要的内容。所有内部目录也被检查到指定的深度。
While I can declare the array Directories0(the input directories) to start, I cannot refer to it in any way... result: bad substitution. Obviously, Directories1would be depth=1, Directories2is depth=2, and so on...
虽然我可以声明数组Directories0(输入目录)开始,但我不能以任何方式引用它......结果:替换错误。显然,Directories1深度=1,Directories2深度=2,依此类推……
Below is a snippet of code:
下面是一段代码:
let Recurse=4 ## say... variable value ##
[ "$Recurse" ] && let MaxDepth="$Recurse" || let MaxDepth=0
declare -i depth=0
IFS=$'\n'
## declare -a Directories${depth}=("${@}") ## <—— doesn't work
## declare -a Directories${depth}="("${@}")" ## <—— works if the brackets only are quoted...
## declare -a Directories${depth}=\("${@}"\) ## <—— ... or escaped
declare -a "Directories${depth}=("${@}")"
IFS=$' \t\n'
## Nested loop, depth counter increases for each directory depth. I want to stop at a specific depth which is entered as an option ##
for (( depth = 0; depth <= MaxDepth; depth++ )); do ## MaxDepth is entered as option ##
until [ -z "${Directories${depth}[*]}" ]; do ## ***** bad substitution error ***** ##
declare input="$(follow "${Directories${depth}[0]}")" ## follow is a script that resolves symlinks and Finder aliases ##
CheckDirectory "${input%/}/" ## check directory ##
case $? in
## Tests passed ##
0) if [[ "$Recurse" && "$depth" -lt "$MaxDepth" ]]; then
IFS=$'\n'
## get ready to check sub-directories ##
declare -a Directories$(( depth + 1 ))="("${Directories$(( depth + 1 ))[@]}" $(find -P "${Directories${depth}[0]}" -type d -mindepth 1 -maxdepth 1 -exec follow '{}' \;))"
IFS=$' \t\n'
fi
true;;
## Tests failed ##
*) false;;
esac
[ $? -eq 0 ] && unset Directories${depth}[0] || exit 1 ## if test fails, exit, if succeeds, move on to next directory ##
declare -a Directories${depth}="("${Directories${depth}[@]}")" ## re-shuffle the array to get rid of null value at index 0 ##
(( element++ ))
done
done
Below is a simplified version in case you don't want to go through the code above, this is the crux of the problem:
下面是一个简化版本,以防你不想通过上面的代码,这是问题的关键:
depth=2
declare -a "Directories${depth}=(yo man ma me mo)"
echo "${Directories${depth}[4]}"
> -bash: ${Directories${depth}[4]}: bad substitution
echo "${Directories2[4]}"
> mo
Solutions, anyone?
解决方案,有人吗?
回答by Gilles 'SO- stop being evil'
You need a literal variable name inside the ${}construct. If you want to refer to a variable whose name is determined at runtime, you need to explicitly go through a level of indirection.
您需要在${}构造中使用文字变量名称。如果要引用名称在运行时确定的变量,则需要显式地经过一个间接级别。
name="Directories${depth}[4]"
echo ${!name}
This doesn't help for assignments. As long as you're assigning in the current scope (and not in an encompassing scope), you can make computed assignments with the typesetbuilt-in. However, be careful: bash has a heuristic which makes it transform assignments that look like the array assignment syntax into array assignments. This means the code below is ok if the element is going to store an absolute file name (which always begins with a /) but not if it is going to store an arbitrary file name (which could be something like (foo)).
这对作业没有帮助。只要您在当前范围内(而不是在包含范围内)进行分配,就可以使用typeset内置函数进行计算分配。但是,请注意:bash 具有启发式方法,可以将看起来像数组赋值语法的赋值转换为数组赋值。这意味着如果元素要存储绝对文件名(始终以 a 开头/),则下面的代码没问题,但如果要存储任意文件名(可能类似于(foo)),则不行。
typeset "Directories${depth}[4]=new value"
It is, alternatively, possible to perform assignments to a variable whose name is determined at runtime with any shell by using eval. This has the advantage of working in any shell, not just bash. You need to be very careful, though: it's hard to get the quoting right. It's best to make the argument of evaldo as little as possible. Use a temporary variable to obtain store the value.
或者,可以使用eval. 这具有在任何 shell 中工作的优势,而不仅仅是 bash。但是,您需要非常小心:很难正确引用。最好让evaldo的参数尽可能少。使用临时变量来获取存储值。
eval "tmp=${Directories${depth}[4]}"
echo "$tmp"
tmp="new value"
eval "Directories${depth}[4]=$tmp"
回答by Diego Torres Milano
Use eval, this works:
使用 eval,这有效:
eval echo ${Directories${depth}[4]}
mo
回答by William Pursell
try eval
尝试评估
#!/bin/bash
depth=2
declare -a "Directories${depth}=(yo man ma me mo)"
eval echo "${Directories${depth}[4]}"
echo "${Directories2[4]}"
回答by ata
You were almost there:
你快到了:
declare -a Directories${depth}="( yo man ma me mo )"
works (and without eval, btw). To access the value, use the ${!} syntax:
有效(并且没有 eval,顺便说一句)。要访问该值,请使用 ${!} 语法:
temp=Directories$depth[@]
echo ${!temp}
回答by michael
Perhaps this is a (belated) answer to what was being asked? (The question is not as clear as the asker believes, I feel....)
也许这是对所问问题的(迟来的)答案?(问题不像提问者认为的那么清楚,我觉得......)
#!/bin/bash
for depth in {0..5}
do
var_value=(yo man ma me mo "last value")
var_name="directories${depth}"
eval "${var_name}=\"${var_value[${depth}]}\""
value="directories${depth}"
printf "directories{$depth} = ${!value}"
[ $depth -eq 0 ] && printf " \t directories0=$directories0\n"
[ $depth -eq 1 ] && printf " \t directories1=$directories1\n"
[ $depth -eq 2 ] && printf " \t directories2=$directories2\n"
[ $depth -eq 3 ] && printf " \t directories3=$directories3\n"
[ $depth -eq 4 ] && printf " \t directories4=$directories4\n"
[ $depth -eq 5 ] && printf " \t directories5=$directories5\n"
done
Which produces:
产生:
directories{0} = yo directories0=yo
directories{1} = man directories1=man
directories{2} = ma directories2=ma
directories{3} = me directories3=me
directories{4} = mo directories4=mo
directories{5} = last value directories5=last value
The main point being that if a variable name consists of another variable, how does one assign a value to it. E.g., if setting "foobar=value" is the normal way to set a variable to a value, then what if x=foo and y=bar, how does one set "${x}${y}=value" => e.g.:
要点是,如果一个变量名由另一个变量组成,那么如何为其赋值。例如,如果设置 "foobar=value" 是将变量设置为值的正常方法,那么如果 x=foo 和 y=bar,如何设置 "${x}${y}=value" =>例如:
foobar=test
echo $foobar
> test
x=foo
y=bar
eval "export ${x}${y}=\"value\""
echo $foobar
> value
If I've misunderstood the question, well, I'm not terribly surprised :-)
如果我误解了这个问题,好吧,我并不感到非常惊讶:-)

