简体   繁体   English

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

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

For a larger project that's not relevant, I need to collect system stats from the local system or a remote system. 对于不相关的大型项目,我需要从本地系统或远程系统收集系统统计信息。 Since I'm collecting the same stats either way, I'm preventing code duplication by storing the stats-collecting commands in a Bash associative array. 由于我以任何一种方式收集相同的统计信息,因此我通过将统计信息收集命令存储在Bash关联数组中来防止代码重复。

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

I can collect local system stats like this: 我可以像这样收集本地系统统计信息:

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

A precondition of my script is that ~/.ssh/config is setup such that ssh $SSH_HOSTNAME works without any user input. 我的脚本的一个前提是~/.ssh/config被设置为ssh $SSH_HOSTNAME在没有任何用户输入的情况下工作。 I would like something like this: 我想要这样的东西:

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

I've tried every combination of single quotes, double quotes, backticks and such that I can imagine. 我已经尝试过单引号,双引号,反引号等各种组合,我可以想象。 Some combinations result in the stats command getting executed too early ( bash: 7986: command not found ), others cause syntax errors, others return null (single quotes around the stats command) but none store the proper result in my data structure. 某些组合导致stats命令执行得太早( bash: 7986: command not found ),其他组合导致语法错误,其他组合返回null(stats命令周围的单引号)但没有将正确的结果存储在我的数据结构中。

How can I evaluate a command, stored in an associative array, on a remote system via SSH and store the result in a data structure in my local script? 如何通过SSH在远程系统上评估存储在关联数组中的命令,并将结果存储在本地脚本的数据结构中?

Make sure that the commands you store in your array don't get expanded when you assign your array! 分配阵列时,请确保阵列中存储的命令不会扩展!

Also note that the complex-looking quoting style is necessary when nesting single quotes. 另请注意,嵌套单引号时需要复杂的引用样式。 See this SO post for an explanation. 请参阅 SO帖子以获得解释。

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

And then just launch your ssh as: 然后启动你的ssh

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

(yeah, I lowercased your variable name because uppercase variable names in Bash are really sick). (是的,我降低了你的变量名,因为Bash中的大写变量名真的很恶心)。 Then: 然后:

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

and

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

First, I'm going to suggest an approach that makes minimal changes to your existing implementation. 首先,我将建议一种对现有实现进行最小改动的方法。 Then, I'm going to demonstrate something closer to best practices. 然后,我将展示更接近最佳实践的东西。


Smallest Modification 最小的修改

Given your existing code: 鉴于您现有的代码:

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

...one can evaluate these locally as follows: ...可以在本地评估这些如下:

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

...or remotely as follows: ......或远程如下:

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

No separate form is required. 不需要单独的表格。


The Right Thing 正确的事情

Now, let's talk about an entirely different way to do this: 现在,我们来谈谈完全不同的方法:

# 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; }

...and then, to evaluate locally: ......然后,在本地评估:

result=$(collect_cpus)

...or, to evaluate remotely: ......或者,远程评估:

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

...or, to iterate through defined functions with the collect_ prefix and do both of these things: ...或者,使用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_)

...or, to collect all the items into a single remote array in one pass, avoiding extra SSH round-trips and not eval 'ing or otherwise taking security risks with results received from the remote system: ...或者,在一次通过中将所有项目收集到一个远程阵列中,避免额外的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