【问题标题】:create arrays from for loop output从 for 循环输出创建数组
【发布时间】:2023-04-01 21:11:01
【问题描述】:

我试图了解我在这里做错了什么,但似乎无法确定原因。我想从输出中为 bash 中的 for 循环创建一组数组。以下是我到目前为止的代码:

for i in `onedatastore list | grep pure02 | awk '{print $1}'`; 
do 
     arr${i}=($(onedatastore show ${i} | sed 's/[A-Z]://' | cut -f2 -d\:)) ; 
     echo "Output of arr${i}: ${arr${i}[@]}" ; 
done

条件的输出如下:

107
108
109

我想做的是基于这些唯一的 ID 创建数组:

arr107
arr108
arr109

每个数组都会有这样的数据:

[oneadmin@opennebula/]$ arr107=($(onedatastore show 107 | sed 's/[A-Z]://' | cut -f2 -d\:))
[oneadmin@opennebula/]$ echo ${arr107[@]}
DATASTORE 107 INFORMATION 107 pure02_vm_datastore_1 oneadmin oneadmin 0 IMAGE vcenter vcenter /var/lib/one//datastores/107 FILE READY DATASTORE CAPACITY 60T 21.9T 38.1T - PERMISSIONS um- u-- --- DATASTORE TEMPLATE CLONE_TARGET="NONE" DISK_TYPE="FILE" DS_MAD="vcenter" LN_TARGET="NONE" RESTRICTED_DIRS="/" SAFE_DIRS="/var/tmp" TM_MAD="vcenter" VCENTER_CLUSTER="CLUSTER01" IMAGES

当我在脚本部分尝试这个时,虽然我得到了这样的输出错误:

./test.sh: line 6: syntax error near unexpected token `$(onedatastore show ${i} | sed 's/[A-Z]://' | cut -f2 -d\:)'

我似乎无法弄清楚在这种情况下要使用的语法。

最后我想做的是能够比较不同的数据存储,并根据哪个有更多的可用空间,将虚拟机部署到它。

希望有人可以提供帮助。谢谢

【问题讨论】:

  • 哪个特定版本的 bash?
  • 请务必阅读Bash FAQ 001

标签: arrays bash loops for-loop


【解决方案1】:

您可以使用eval(可能不安全)和declare(更安全)命令:

for i in $(onedatastore list | grep pure02 | awk '{print $1}'); 
do 
    declare "arr$i=($(onedatastore show ${i} | sed 's/[A-Z]://' | cut -f2 -d\:))"
    eval echo 'Output of arr$i: ${arr'"$i"'[@]}'
done

【讨论】:

  • 现在,这可能容易出现安全漏洞,因为它会双重评估内部命令替换的输出。该命令替换应该被转义为仅在 after eval 被调用时运行,而不是 before
  • 顺便说一句,如果您的目标是 bash 4.3,使用名称变量可以轻松访问 arr$i。就此而言,写作也是如此。
  • 明确地说,eval 'arr'"$i"'=($(onedatastore show ${i} | sed "s/[A-Z]://" | cut -f2 -d\:))' 是一个更安全版本的示例。 (它仍然有问题,因为它使用字符串拆分来形成一个数组,因此在没有用户要求的情况下扩展 glob,但这是一个完全不同的问题)。
  • 感谢您的提示。 declare 显然是更好的选择。答案已更新。
  • 谢谢。不仅修复了数组创建的问题,还修复了 Charles 指出的安全问题并使用了 declare。这修复了 echo 的输出,因为我遇到了替换错误的问题。
【解决方案2】:

readarraymapfile,在 bash 4.0 中添加,将直接读入数组:

while IFS= read -r i <&3; do
  readarray -t "arr$i" < <(onedatastore show "$i" | sed 's/[A-Z]://' | cut -f2 -d:)
done 3< <(onedatastore list | awk '/pure02/ {print $1}')

更好,回到 bash 3.x,可以使用read -a 读取数组:

shopt -s pipefail # cause pipelines to fail if any element does

while IFS= read -r i <&3; do
  IFS=$'\n' read -r -d '' -a "arr$i" \
    < <(onedatastore show "$i" | sed 's/[A-Z]://' | cut -f2 -d: && printf '\0')
done 3< <(onedatastore list | awk '/pure02/ {print $1}')

或者,在 bash 4.3 中,可以使用 namevars 为具有任意命名数组的数组创建别名:

while IFS= read -r i <&3; do
  declare -a "arr$i"
  declare -n arr="arr$i"
  # this is buggy: expands globs, string-splits on all characters in IFS, etc
  # ...but, well, it's what the OP is asking for...
  arr=( $(onedatastore show "$i" | sed 's/[A-Z]://' | cut -f2 -d:) )
done 3< <(onedatastore list | awk '/pure02/ {print $1}')

【讨论】:

  • 我也喜欢这个答案。唯一的问题是尝试完成输出,因为我需要比较每个数组中的字段以确定哪个数据存储使用最少。我会 +1 只是因为你提供了 bash 3.x 及更低版本的机制,即使没有被问到,而且我的问题也没有明确说明。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-11
  • 2021-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-28
  • 1970-01-01
相关资源
最近更新 更多