这里出现一个奇怪的错误。我有一些像这样的 json 结构:
BACKUP_REPOS='''
[
{"name":"my.server1", "hash":"server1-hash"},
{"name":"my.server2", "hash":"server2-hash"},
{"name":"my.server3", "hash":"server3-hash"}
]
'''
我试图做的是基于使用jq
以下函数生成一个可选择的菜单:
# generate the server menu
server_menu( ) {
# hold some arrays
declare -a _name _hash;
while read -r _server _pw; do
# hold the selected item
_name[$_server]=$_server;
_hash[$_server]=$_pw;
# end the loop generation
done < <( jq -rc '.[] | "\(.name) \(.hash)"' <<< "$BACKUP_REPOS" )
# now loop the resultset
select _server in "${!_name[@]}"; do
[ -n "$_server" ] && break;
done;
# return the server and hash
SELECTED_SERVER=${_name[$_server]};
SELECTED_HASH=${_hash[$_server]};
}
现在,这在 Ubuntu 18+ 中工作得很好,但在我的 Mac BigSur 终端上会生成错误。错误是:syntax error: invalid arithmetic operator (error token is ".server1") on line 82
,在我的完整代码文件中是这一行:_name[$_server]=$_server;
我这里哪里出错了?此时,我唯一关心的是让这个脚本在我的 Mac 上运行...我可以保留它的多个副本
TIA
答案1
看起来您正在尝试使用关联数组,这在 macOS 附带的默认 bash 版本上是不可能的,因此您必须将 bash 更新到版本 4 或更高版本才能执行此操作,但您还需要更改:
declare -a _name _hash
到
declare -A _name _hash
作为标准数组,索引中唯一可以包含的内容是整数,并且您可以在其中执行算术扩展,[ ... ]
因此它会因您在其中扩展的非整数/无效操作而窒息。
答案2
作为jesse_b 指出,问题是您的数组不是关联数组。 macOS上bash
不知道如何处理关联数组,所以考虑重写zsh
或者使用普通数组。或者,bash
使用 Homebrew 安装更新的 shell 并将其更正declare -a
为declare -A
.
以下内容适用于bash
macOS 上的默认设置(假设jq
也已安装):
menu_select_server () {
local server_json=file.json # JSON containing server config
# Set positional parameters to list of servers
eval "$( jq -r '[ "set --", (.[].name|@sh) ] | @tsv' "$server_json" )"
# Select wanted server
local PS3='Select server: '
select SELECTED_SERVER; do
[[ -n $SELECTED_SERVER ]] && break
done
# Get corresponding hash
SELECTED_HASH=$(
jq -r --argjson i "$REPLY" \
'.[($i-1)].hash' "$server_json"
)
}
我们在这里解析文件两次,一次获取服务器列表,一次获取所选服务器的哈希值,但是很容易做与您正在做的类似的事情,只需切换到使用$REPLY
来自select
循环获取哈希值:
menu_select_server () {
local server_json=file.json # JSON containing server config
local name hash
local hashes=()
set --
# Read names into list of positional parameters
# Read hashes into "hashes" array
while IFS=$'\t' read -r name hash; do
set -- "$@" "$name"
hashes+=( "$hash" )
done < <( jq -r '.[] | [ .name, .hash ] | @tsv' "$server_json" )
# or:
# jq -r 'map([ .name, .hash ] | @tsv)[]' "$server_json"
# Select wanted server
local PS3='Select server: '
select SELECTED_SERVER; do
[[ -n $SELECTED_SERVER ]] && break
done
# Get corresponding hash
SELECTED_HASH=${hashes[REPLY-1]}
}