簡體   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