繁体   English   中英

通过SSH执行存储在关联数组中的bash命令,存储结果

[英]Execute bash command stored in associative array over SSH, store result

对于不相关的大型项目,我需要从本地系统或远程系统收集系统统计信息。 由于我以任何一种方式收集相同的统计信息,因此我通过将统计信息收集命令存储在Bash关联数组中来防止代码重复。

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]}`
}

我已经尝试过单引号,双引号,反引号等各种组合,我可以想象。 某些组合导致stats命令执行得太早( bash: 7986: command not found ),其他组合导致语法错误,其他组合返回null(stats命令周围的单引号)但没有将正确的结果存储在我的数据结构中。

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

分配阵列时,请确保阵列中存储的命令不会扩展!

另请注意,嵌套单引号时需要复杂的引用样式。 请参阅 SO帖子以获得解释。

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

然后启动你的ssh

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

(是的,我降低了你的变量名,因为Bash中的大写变量名真的很恶心)。 然后:

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]}")
}

首先,我将建议一种对现有实现进行最小改动的方法。 然后,我将展示更接近最佳实践的东西。


最小的修改

鉴于您现有的代码:

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")

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM