执行存储在通过SSH,结果存储关联数组bash命令(Execute bash command sto

2019-10-20 07:44发布

对于较大的项目,该项目是不相关的,我需要从本地系统或远程系统收集系统的统计数据。 由于我收集相同统计无论哪种方式,我被存储在击关联数组的统计信息收集命令防止代码重复。

declare -A stats_cmds
# Actually contains many more key:value pairs, similar style
stats_cmds=([total_ram]="$(free -m | awk '/^Mem:/{print $2}')")

我可以收集本地系统统计数据是这样的:

get_local_system_stats()
{
    # Collect stats about local system
    complex_data_structure_that_doesnt_matter=${stats_cmds[total_ram]}
    # Many more similar calls here
}

我的脚本的前提是~/.ssh/config是这样设置,从而ssh $SSH_HOSTNAME工作没有任何用户输入。 我想是这样的:

get_remote_system_stats()
{
    # Collect stats about remote system
    complex_data_structure_that_doesnt_matter=`ssh $SSH_HOSTNAME ${stats_cmds[total_ram]}`
}

我试过单引号,双引号,反引号并使得我能想象的所有组合。 一些组合导致统计命令得到执行太早( bash: 7986: command not found ),其他导致语法错误,其他人在我的数据结构返回null(围绕统计单引号命令),但没有商店正确的结果。

如何通过SSH评估命令,存储在一个关联数组,在远程系统上,结果在一个数据结构存储在我的本地脚本?

Answer 1:

首先,我要提出一个方法,使得最小的更改您现有的实现。 然后,我要证明的东西更接近最佳实践。


最小修改

鉴于现有的代码:

declare -A remote_stats_cmds
remote_stats_cmds=([total_ram]='free -m | awk '"'"'/^Mem:/{print $2}'"'"''
            [used_ram]='free -m | awk '"'"'/^Mem:/{print $3}'"'"''
            [free_ram]='free -m | awk '"'"'/^Mem:/{print $4}'"'"''
            [cpus]='nproc'
            [one_min_load]='uptime | awk -F'"'"'[a-z]:'"'"' '"'"'{print $2}'"'"' | awk -F "," '"'"'{print $1}'"'"' | tr -d " "'
            [five_min_load]='uptime | awk -F'"'"'[a-z]:'"'"' '"'"'{print $2}'"'"' | awk -F "," '"'"'{print $2}'"'"' | tr -d " "'
            [fifteen_min_load]='uptime | awk -F'"'"'[a-z]:'"'"' '"'"'{print $2}'"'"' | awk -F "," '"'"'{print $3}'"'"' | tr -d " "'
            [iowait]='cat /proc/stat | awk '"'"'NR==1 {print $6}'"'"''
            [steal_time]='cat /proc/stat | awk '"'"'NR==1 {print $9}'"'"'')

...可以如下本地评估这些:

result=$(eval "${remote_stat_cmds[iowait]}")
echo "$result" # demonstrate value retrieved

...或者远程如下:

result=$(ssh "$hostname" bash <<<"${remote_stat_cmds[iowait]}")
echo "$result" # demonstrate value retrieved

不需要单独的形式。


正确的事情

现在,让我们来谈谈一个完全不同的方式做到这一点:

# no awful nested quoting by hand!
collect_total_ram() { free -m | awk '/^Mem:/ {print $2}'; }
collect_used_ram()  { free -m | awk '/^Mem:/ {print $3}'; }
collect_cpus()      { nproc; }

......然后,在本地评价:

result=$(collect_cpus)

...或者,可以远程评估:

result=$(ssh "$hostname" bash <<<"$(declare -f collect_cpus); collect_cpus")

...或者,通过自定义函数与迭代collect_前缀和两者都做的这些事情:

declare -A local_results
declare -A remote_results
while IFS= read -r funcname; do
  local_results["${funcname#collect_}"]=$("$funcname")
  remote_results["${funcname#collect_}"]=$(ssh "$hostname" bash <<<"$(declare -f "$funcname"); $funcname")
done < <(compgen -A function collect_)

...或者,收集所有的物品放入一个遥控器阵列中的一个传球,避免了额外的SSH往返,而不是eval “荷兰国际集团或以其他方式获得的安全隐患从远程系统接收到的结果:

remote_cmd=""
while IFS= read -r funcname; do
  remote_cmd+="$(declare -f "$funcname"); printf '%s\0' \"$funcname\" \"\$(\"$funcname\")\";"
done < <(compgen -A function collect_)

declare -A remote_results=( )
while IFS= read -r -d '' funcname && IFS= read -r -d '' result; do
  remote_results["${funcname#collect_}"]=$result
done < <(ssh "$hostname" bash <<<"$remote_cmd")


Answer 2:

请确保您的数组中存储你的命令没有得到当你将你的阵列扩展!

还要注意的是嵌套单引号当复视引用风格是必要的。 请参见本 SO张贴的解释。

stats_cmds=([total_ram]='free -m | awk '"'"'/^Mem:/{print $2}'"'"'')

然后,只需启动你的ssh为:

sh "$ssh_hostname" "${stats_cmds[total_ram]}"

(是的,我小写的变量名,因为猛砸大写变量名是真的生病了)。 然后:

get_local_system_stats() {
    # Collect stats about local system
    complex_data_structure_that_doesnt_matter=$( ${stats_cmds[total_ram]} )
    # Many more similar calls here
}

get_remote_system_stats() {
    # Collect stats about remote system
    complex_data_structure_that_doesnt_matter=$(ssh "$ssh_hostname" "${stats_cmds[total_ram]}")
}


文章来源: Execute bash command stored in associative array over SSH, store result