2个叉子

2个叉子

有没有办法打印整个数组([key]=value)而不循环遍历所有元素?

假设我创建了一个包含一些元素的数组:

declare -A array
array=([a1]=1 [a2]=2 ... [b1]=bbb ... [f500]=abcdef)

我可以打印回整个数组

for i in "${!array[@]}"
do
echo "${i}=${array[$i]}"
done

然而,bash 似乎已经知道如何一次性获取所有数组元素——键${!array[@]}和值${array[@]}

有没有办法让 bash 在没有循环的情况下打印这些信息?

编辑:
typeset -p array这样做!
但是我无法在一次替换中同时删除前缀和后缀:

a="$(typeset -p array)"
b="${a##*(}"
c="${b%% )*}"

有没有更干净的方法来仅获取/打印输出的 key=value 部分?

答案1

我认为你在那里问两件不同的事情。

有没有办法让 bash 在没有循环的情况下打印这些信息?

是的,但它们不如仅使用循环好。

有没有更干净的方法来仅获取/打印输出的 key=value 部分?

是的,for循环。它的优点是不需要外部程序,简单,并且可以很容易地控制精确的输出格式而不会出现意外。


任何尝试处理declare -p( typeset -p) 输出的解决方案都必须处理 a) 变量本身包含圆括号或方括号的可能性,b)declare -p必须添加的引用以使其输出成为 shell 的有效输入。

例如,b="${a##*(}"如果任何键/值包含左括号,则您的扩展会吃掉一些值。这是因为您使用了##,它删除了最长字首。对于 也一样c="${b%% )*}"。尽管您当然可以declare更准确地匹配打印的样板,但如果您不想要它所引用的所有内容,您仍然会遇到困难。

除非你需要它,否则这看起来不太好。

$ declare -A array=([abc]="'foobar'" [def]='"foo bar"')
$ declare -p array
declare -A array='([def]="\"foo bar\"" [abc]="'\''foobar'\''" )'

使用for循环,可以更轻松地根据需要选择输出格式:

# without quoting
$ for x in "${!array[@]}"; do printf "[%s]=%s\n" "$x" "${array[$x]}" ; done
[def]="foo bar"
[abc]='foobar'

# with quoting
$ for x in "${!array[@]}"; do printf "[%q]=%q\n" "$x" "${array[$x]}" ; done
[def]=\"foo\ bar\"
[abc]=\'foobar\'

从那里开始,也是简单的否则更改输出格式(删除键周围的括号,将所有键/值对放在一行上...)。如果您需要引用 shell 本身以外的其他内容,您仍然需要自己完成,但至少您拥有可以处理的原始数据。 (如果键或值中有换行符,则可能需要一些引用。)

printf "[%s]=%s" "${x@Q}" "${array[$x]@Q}"对于当前的 Bash(我认为是 4.4),您也可以使用printf "%q=%q".它产生了一种更好的引用格式,但当然需要记住编写更多的工作。 (它引用了as 数组键的极端情况,但它不引用。)@%q

如果 for 循环看起来太累而无法编写,请将其保存为一个函数(此处不引用):

printarr() { declare -n __p="$1"; for k in "${!__p[@]}"; do printf "%s=%s\n" "$k" "${__p[$k]}" ; done ;  }  

然后使用它:

$ declare -A a=([a]=123 [b]="foo bar" [c]="(blah)")
$ printarr a
a=123
b=foo bar
c=(blah)

也适用于索引数组:

$ b=(abba acdc)
$ printarr b
0=abba
1=acdc

答案2

declare -p array
declare -A array='([a2]="2" [a1]="1" [zz]="Hello World" [b1]="bbb" [f50]="abcd" )'

2个叉子

也许这个:

printf "%s\n" "${!array[@]}"
a2
a1
f50
zz
b1

printf "%s\n" "${array[@]}"
2
1
abcd
Hello World
bbb

printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t
a2                              2
a1                              1
f50                             abcd
zz                              Hello World
b1                              bbb

3 个叉子

或这个:

paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}")
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb

没有叉子

来比较

for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb

执行时间比较

由于最后的语法不使用 fork,它们可能会更快:

time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
      5      11      76
real    0m0.005s
user    0m0.000s
sys     0m0.000s

time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
      5       6      41
real    0m0.008s
user    0m0.000s
sys     0m0.000s

time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
      5       6      41
real    0m0.002s
user    0m0.000s
sys     0m0.001s

但如果数组变大,这个断言就不再成立。如果减少分叉对于小型流程来说是有效的,那么使用专用工具对于较大的流程来说会更有效。

for i in {a..z}{a..z}{a..z};do array[$i]=$RANDOM;done


time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
  17581   35163  292941
real    0m0.150s
user    0m0.124s
sys     0m0.036s

time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
  17581   17582  169875
real    0m0.140s
user    0m0.000s
sys     0m0.004s

time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
  17581   17582  169875
real    0m0.312s
user    0m0.268s
sys     0m0.076s

评论

既 (分叉的)解决方案使用结盟,如果任何变量包含新队。在这种情况下,唯一的方法就是for循环。

StackOverflow 上有更可靠、更详细的答案

答案3

KBash 5.1 允许通过使用如下所示的值以非常直接的方式显示关联数组${arr[@]@K}

$ declare -A arr
$ arr=(k1 v1 k2 v2)
$ printf "%s\n" "${arr[@]@K}"
k1 "v1" k2 "v2" 

来自Bash 5.1 描述文档

呵呵。新的“K”参数转换将关联数组显示为键值对。

中对此有很好的解释Bash 参考手册 → 3.5.3 Shell 参数扩展

${parameter@operator}

K
生成参数值的可能引用版本,不同之处在于它将索引数组和关联数组的值打印为引用键值对的序列(请参阅数组)。

答案4

自从排版做你想要的为什么不直接编辑它的输出呢?

typeset -p array | sed s/^.*\(// | tr -d ")\'\""  | tr "[" "\n" | sed s/]=/' = '/

给出

a2 = 2  
a1 = 1  
b1 = bbb 

在哪里

array='([a2]="2" [a1]="1" [b1]="bbb" )'

冗长,但很容易看出格式化是如何工作的:只需使用逐渐更多的内容执行管道即可sedt命令。修改它们以适应漂亮的打印品味。

相关内容