基于另一个关联数组制作关联数组

mf9*_*f94 3 bash associative-array

我制作了一个关联数组,如下所示。为了提供一些详细信息,键是指特定文件,因为我将在较大脚本的上下文中使用此数组(其中包含文件的目录将是 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
Run Code Online (Sandbox Code Playgroud)

到目前为止,这个数组的行为似乎符合我的预期。现在,我想基于这个数组构建另一个关联数组。具体来说:我的第二个数组将具有与第一个相同的键,但我想将这些值除以名为 $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
Run Code Online (Sandbox Code Playgroud)

这不是我想要的钥匙。即使它有效,我也需要对所有值执行我提到的操作。

策略 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
Run Code Online (Sandbox Code Playgroud)

谢谢

Sté*_*las 6

回答关于复制关联数组的更普遍的问题。

bash维护者在 4.0 中引入自己的关联数组时,他们做出了一个不幸的决定,即复制 ksh93 API 而不是 zsh API。

ksh93/bash确实支持将关联数组设置为一个整体,但它与:

hash=([k1]=v1 [k2]=v2)
Run Code Online (Sandbox Code Playgroud)

句法。使用 时zsh,它是

hash=(k1 v1 k2 v2)
Run Code Online (Sandbox Code Playgroud)

([k]=v...)为了兼容性,稍后还添加了对ksh93 语法的支持)。

不过,这意味着使用 ksh93 和 bash,从任意键和值列表中创建散列是非常棘手的。

使用zsh语法,您只需要将列表作为交替键和值传递。例如,要复制两个关联数组:

h2=("${(@kv)h1}")
Run Code Online (Sandbox Code Playgroud)

或者来自包含两列的 CSV:

IFS=$'\n,'; h=($(<file.csv))
Run Code Online (Sandbox Code Playgroud)

或者来自键和值的数组:

h=("${(@)keys:^values}")
Run Code Online (Sandbox Code Playgroud)

使用ksh93/bash语法,虽然有"${!h[@]}"and"${h[@]}"扩展到键和值列表(如"${(@k)h}"and "${(@v)h}"in zsh),但没有运算符可以扩展到(in )[key]=value预期的语法中的键和值。h=(...)"${(@kv)h}"zsh

您可以在这些 shell 中使用的一个技巧来复制关联数组(除了在循环中复制元素),是使用typeset -p.

举例来说,相当于zshh2=("${(@kv)h1}")复制h1h2中可以做ksh93bash用:

h1_definition=$(typeset -p h1) &&
  eval "typeset -A h2=${h1_definition#*=}"
Run Code Online (Sandbox Code Playgroud)

bash可以缩短到:

h1_definition=$(typeset -p h1) &&
  typeset -A h2="${h1_definition#*=}"
Run Code Online (Sandbox Code Playgroud)

(虽然像在ksh93的,typeset -A h=value是短于typeset -A h=([0]=value)bash,如果value与开始(和结束用),然后仿佛传递到内容被解释为化合物缔分配eval(即使该(被引用或一些扩展的结果))。

最后,使用循环也很容易:

for k in "${!h1[@]}"; do h2[$k]=${h1[$k]}; done
Run Code Online (Sandbox Code Playgroud)


Kus*_*nda 5

从旧数组构建新数组:

MIN=33285268

declare -A BRAMFRACS
for key in "${!BAMREADS[@]}"; do
    BRAMFRACS[$key]=$(( ${BAMREADS[$key]} / MIN ))
done
Run Code Online (Sandbox Code Playgroud)

对您的代码的评论:

  • 您的第一个建议代码不起作用,因为它将从关联数组复制到新数组。这些值会自动获取键 0、1 和 2,但不会复制原始键。如上所示,您需要逐个键复制数组。这样您就可以将所需的值分配给正确的键。

  • 您的第二个建议代码包含一个语法错误,因为它=在赋值中有空格。这就是您看到的错误的来源。 variable = value被解释为“variable使用操作数=value”执行的命令。

  • 如果您希望迭代一组路径名,请不要使用ls. 而只是做for pathname in "$BAMFILES"/*bam; do

  • 引用你的变量扩展。

  • 考虑使用printf而不是echo输出可变数据。

有关的: