我制作了一个关联数组,如下所示。为了提供一些详细信息,键引用特定文件,因为我将在较大脚本的上下文中使用此数组(其中包含文件的目录将是 getopts 参数)。
declare -A BAMREADS
echo "BAMREADS array is initialized"
BAMREADS[../data/file1.bam]=33285268
BAMREADS[../data/file2.bam]=28777698
BAMREADS[../data/file3.bam]=22388955
echo ${BAMREADS[@]} # Output: 22388955 33285268 28777698
echo ${!BAMREADS[@]} # Output: ../data/file1.bam ../data/file2.bam ../data/file3.bam
到目前为止,这个数组的行为似乎符合我的预期。现在,我想基于这个数组构建另一个关联数组。具体来说:我的第二个数组将具有与第一个数组相同的键,但我想将这些值除以一个名为 $MIN 的变量。
我不确定以下哪种策略是最好的,而且我似乎都无法使这两种策略发挥作用。
策略1:复制数组,修改数组?
MIN=33285268
declare -A BRAMFRACS
echo "BAMFRACS array is initialized"
BAMFRACS=("${BAMREADS[@]}")
echo ${BAMFRACS[@]} # Output: 22388955 33285268 28777698
echo ${!BAMFRACS[@]} # Output: 0 1 2
这不是我想要的钥匙。即使它有效,我也需要对所有值执行我提到的操作。
策略2:循环第一个数组时构建第二个数组。
MIN=33285268
declare -A BRAMFRACS
echo "BAMFRACS array is initialized"
for i in $(ls $BAMFILES/*bam)
do
echo $i
echo ${BAMREADS[$i]}
BAMFRACS[$i] = ${BAMREADS[$i]}
done
echo ${BAMFRACS[@]}
echo ${!BAMFRACS[@]}
#When I run this, I get the following error which I am unsure how to solve:
../data/file1.bam
33285268
script.bash: line 108: BAMFRACS[../data/file1.bam]: No such file or directory
../data/file2.bam
28777698
script.bash: line 108: BAMFRACS[../data/file2.bam]: No such file or directory
../data/file3.bam
22388955
script.bash: line 108: BAMFRACS[../data/file3.bam]: No such file or directory
谢谢
答案1
回答有关复制关联数组的更普遍的问题。
当维护人员在 4.0 中引入自己的关联数组时,他们bash
做出了一个不幸的决定,即复制 ksh93 API 而不是 zsh API。
ksh93
/bash
确实支持将关联数组设置为一个整体,但它与:
hash=([k1]=v1 [k2]=v2)
句法。与 时zsh
,它是
hash=(k1 v1 k2 v2)
(([k]=v...)
为了兼容性,后来还添加了对 ksh93 语法的支持)。
但这意味着,对于 ksh93 和 bash,从任意键和值列表创建哈希是非常棘手的。
使用zsh
语法,您只需将列表作为交替键和值传递即可。例如,要复制两个关联数组:
h2=("${(@kv)h1}")
或者来自包含两列的 CSV:
IFS=$'\n,'; h=($(<file.csv))
或者来自键和值的数组:
h=("${(@)keys:^values}")
使用ksh93
/bash
语法时,虽然有"${!h[@]}"
and"${h[@]}"
可以扩展到键和值的列表(如"${(@k)h}"
and "${(@v)h}"
in zsh
),但没有运算符可以扩展到(in )[key]=value
所期望的语法中的键和值。h=(...)
"${(@kv)h}"
zsh
您可以在这些 shell 中使用的一个技巧来复制关联数组(而不是在循环中复制元素),即使用typeset -p
.
例如,复制到zsh
的等效操作可以通过以下方式完成:h2=("${(@kv)h1}")
h1
h2
ksh93
bash
h1_definition=$(typeset -p h1) &&
eval "typeset -A h2=${h1_definition#*=}"
您bash
可以将其缩短为:
h1_definition=$(typeset -p h1) &&
typeset -A h2="${h1_definition#*=}"
(虽然像 ksh93 中一样,是intypeset -A h=value
的缩写,如果以 开头并以 结尾,则内容将被解释为复合关联赋值,就好像传递给一样(即使被引用或某些扩展的结果))。typeset -A h=([0]=value)
bash
value
(
)
eval
(
最后,使用循环也同样容易:
for k in "${!h1[@]}"; do h2[$k]=${h1[$k]}; done
答案2
从旧数组构建新数组:
MIN=33285268
declare -A BRAMFRACS
for key in "${!BAMREADS[@]}"; do
BRAMFRACS[$key]=$(( ${BAMREADS[$key]} / MIN ))
done
对您的代码的评论:
您的第一个建议的代码不起作用,因为它复制了价值观从关联数组到新数组。这些值会自动获取键 0、1 和 2,但不会复制原始键。您需要按键复制数组,如我上面所示。这样您就可以将所需的值分配给正确的键。
您的第二个建议代码包含语法错误,因为它
=
在赋值周围有空格。这就是您看到的错误的来源。 被解释为“使用操作数和执行的variable = value
命令”。variable
=
value
如果您希望迭代一组路径名,不要使用
ls
。相反,只需执行for pathname in "$BAMFILES"/*bam; do
.引用你的变量扩展。
考虑使用
printf
而不是echo
输出可变数据。
有关的:
答案3
以下 bash 将关联数组 AA2(可能未设置)中的值分配给另一个关联数组 AA1(大部分必须声明为 -A)。
LIST="$(declare -p AA2 2>/dev/null)"
[[ "$LIST" ]] && AA1+=${LIST#*=}
declare -p
将变量的值作为declare
语句回显,可以直接传递到解释器中,而不会出现任何分词问题。${LIST#*=}
删除等号及其前面的所有内容。- 在非零长度测试 (
2>/dev/null
) 期间隐藏错误 ( )允许取消设置 AA2。declare
[[ "$LIST" ]]
- 如果 AA1 或 AA2 不是关联数组,则会出现意外结果(不是错误)。
答案4
这应该可以做到(也可以添加额外的键值):
declare -A origDict=( [keya]=value_a [keyb]=value_b [keyc]=value_c )
declare -a newDict=( echo ${origDict[*]} [keynew]=new_value )