这个问题来自于减少 bash 脚本中重复的愿望,围绕检查文件中是否有某些内容,如果有,则将其第一行加载到同名变量中。
目前,对于整个过程中使用的每个变量,有几十这样的行:
[[ -s './config/foo' ]] && read -r foo < './config/foo' || echo "problem with ./config/foo file"
[[ -s './config/bar' ]] && read -r foo < './config/bar' || echo "problem with ./config/foo file"
但是,我认为我可以通过使用数组来自动化此过程,而不是像这样经历太多行(稍后,使用此方法扩展到其他测试)。
所以我开始编写下面的代码,但一直想知道是否可以动态创建变量名?我不知道该怎么做,或者是否可能。我知道我可以通过使用
${file##*/}
剥离路径来获取我们想要用于创建变量名称的文件的名称(例如,./config/foo
变为 foo
),但是如何将该结果转换为一个变量名,然后将其设置为文件第一行的内容,就像原始脚本一样?
这是我到目前为止所得到的,其中
DYNAMIC_VARIABLE_NAME_HERE
是我们可以从 ${file##*/}
获得的名称:
#!/bin/bash
# check and load config files
required_files=(
'./config/foo'
'./config/bar'
)
for file in "${required_files[@]}"; do
[[ -s "${file}" ]] && read -r DYNAMIC_VARIABLE_NAME_HERE < "${file}" || failed+=("${file}")
done
if [[ ${#failed[@]} -ne 0 ]]; then
echo "there is a problem with the following configuration files:"
for file in "${failed[@]}"; do
echo "${file}"
done
fi
# check
echo "foo: ${foo}"
echo "bar: ${bar}"
foo:
bar:
foo: [first line of ./config/foo file]
bar: [first line of ./config/bar file]
设置:
$ head foo bar
==> foo <==
1st line from foo
2nd line from foo
==> bar <==
1st line from bar
2nd line from bar
如果使用
bash 4.2+
,您可以使用名称引用,例如:
for fname in foo bar
do
declare -n curr_var="${fname}"
read -r curr_var < "${fname}"
done
这会生成:
$ typeset -p foo bar
declare -- foo="1st line from foo"
declare -- bar="1st line from bar"
这种方法的一个问题是……如何跟踪动态生成的变量名称。在本例中,我们在代码中硬编码了变量名称
foo
和 bar
,但是如果有 10 个文件怎么办……如何/在哪里跟踪变量名称?
使用关联数组的不同方法:
declare -A myvar
for fname in * # for now assume this matches on files "foo" and "bar"
do
read -r myvar[$fname] < "${fname}"
done
这会生成:
$ typeset -p myvar
declare -A myvar=([bar]="1st line from bar" [foo]="1st line from foo" )
此外,我们可以通过仔细阅读数组的索引来获取文件名列表,例如:
for varname in "${!myvar[@]}"
do
echo "varname = ${varname} : contents: ${myvar[$ndx]}"
done
这会生成:
varname = bar : contents: 1st line from foo
varname = foo : contents: 1st line from foo