Bash脚本抱怨文件名太长。

6

我有一个脚本可以实现这个功能(jq是一个命令行JSON处理器):

echo "Getting LB Node IDs"
echo $LB_STATUS | jq '.loadBalancer.nodes[] .id'

最后一行的输出是:
1
2
3

但是当我尝试将它赋值给一个数组:

echo "Creating an Array"
nodeIdArray=($($LB_STATUS | jq '.loadBalancer.nodes[] .id'))

我收到了这个错误:

./myShellScript.sh: line 53: {"loadBalancer":{"name":"lbName","id":1,"protocol":"HTTP","port":80,"algorithm":"WEIGHTED_LEAST_CONNECTIONS","status":"ACTIVE","cluster":{"name":"ztm-n22.dfw1.lbaas.rackspace.net"},"nodes":[{"address":"1.2.3.4","id":1,"type":"PRIMARY","port":80,"status":"ONLINE","condition":"ENABLED","weight":1},{"address":"1.2.3.4","id":2,"type":"PRIMARY","port":80,"status":"ONLINE","condition":"ENABLED","weight":1},{"address":"1.2.3.4","id":3,"type":"PRIMARY","port":80,"status":"ONLINE","condition":"ENABLED","weight":1}],"timeout":30,"created":{"time":"2016-06-28T22:14:24Z"},"healthMonitor":{"type":"CONNECT","delay":10,"timeout":5,"attemptsBeforeDeactivation":2},"sslTermination":...<A BOAT LOAD MORE JSON I CUT OUT FOR BREVITY'S SAKE>: File name too long

当尝试将SO $LB_STATUS | jq '.loadBalancer.nodes[] .id'产生的一些数字分配给数组时,会失败。


$LB_STATUS 运行的结果是将其扩展为一个命令,而不是将其作为数据传递。 - Charles Duffy
"$LB_STATUS | jq ..." 不是 echo 的参数;它是由管道连接的两个命令 echojq。你的赋值是完全错误的。 - chepner
在给数组赋值时,您忘记了 echo 命令。 - Jack
2个回答

16

出了什么问题

$variable | something不能将variable中的文本作为输入传递给something,而是将$variable的内容作为命令运行。你可能想使用echo "$variable" | something代替(但请参见下面!)

即使这个问题得到解决,array=( $(some-command) )习惯用法本身也有缺陷。请参阅BashPitfalls #50,了解为什么不应该使用它以及各种替代方法。

应该怎么做

当将变量中的内容作为命令的输入时,使用herestring是惯用法:somecommand <<<"$variable"。虽然它们会创建临时文件,但比管道更便宜(管道会派生子shell)。

如果你使用的是bash 4.x或更高版本,则可以使用readarray

readarray -t nodeIdArray < <(jq -r '.loadBalancer.nodes[].id' <<<"$LB_STATUS")

如果需要与bash 3.x兼容,read -a可以完成该任务:
IFS=$'\n' read -r -d '' -a nodeIdArray \
  < <(jq -r '.loadBalancer.nodes[].id' <<<"$LB_STATUS" && printf '\0')

这还有一个优点,即使 jq 命令失败,也会导致 read 返回非零的退出状态。


1
假设这些只是简单的数字ID,我会使用jq -rc '[.loadBalancer.nodes[].id] | map(tostring) | join(" ")'来生成更友好的输入,以便于read -a使用。 - chepner
你的解决方案很简单,而且有效。aalku的也是(尽管他已经删除了)。但还是谢谢你。我不知道为什么我完全忽略了前一行中的echo是如何通过管道传递给jq的...感觉像业余时间在这里。 - Jerry Skidmore

8

你漏掉了echo

修改为

nodeIdArray=($($LB_STATUS | jq '.loadBalancer.nodes[] .id'))

to

nodeIdArray=($( echo $LB_STATUS | jq '.loadBalancer.nodes[] .id' ))

1
阅读查尔斯的答案,我注意到...现在看起来似乎很明显,不确定为什么我之前会遇到困难。 - Jerry Skidmore
是的,他有点跑题了。他讨厌那种分配数组的方法。我已经做这些东西超过三十年了,从来没有遇到过问题。当然,这可能是因为我已经做这些东西超过三十年了。 - Jack
1
@Jack 在这种情况下它能够工作是因为你“知道”在路径名扩展之后命令的输出不会改变;并非所有阅读答案的人都意识到潜在的陷阱,他们可能会尝试将代码适应到它不能工作的情况。 - chepner
@Jack,我只见过一次由于shell脚本在不应该发生时没有小心避免通配符而导致的大规模数据丢失事件,在过去20年中。但是那是个灾难 - 十几TB的账单备份记录被删除。这也可能是其他(长期经验的)员工看到的由如此微小的bug引起的如此严重的破坏的唯一一次,但仅此一次就足以造成数周时间的工作来重建旧数据。仅仅因为通常可以擅自懒惰的处理方法行得通,并不意味着我们应该这样做。 - Charles Duffy
@Jack,...之前提到的情况是一个人“知道”变量的内容只可能是与[0-9a-f]{24}匹配的值,直到另一个带有错误库的程序将内存中的垃圾转储到用于文件名的缓冲区中,而一些“不可能”的事情发生了。现在,我正在从事安全工作--如果有人假设一个边角情况“不可能”发生,那么其他人就可以努力找出他们如何利用这种假设。 - Charles Duffy
这就是为什么一些公司愿意付出丰厚的薪酬来雇用具有30年以上经验的人,而其他一些公司则为雇用经验较少的人付出高昂的代价。 - Jack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接