在沒有 bc 實用程序的情況下複製浮點運算
我需要在一些尚未
bc
安裝且無法安裝的嵌入式 Linux 系統上編寫腳本。我正在編寫的腳本本質上是一個監控腳本,它在不同的負載值下採取某些糾正措施。例如,在平均負載為 1.5 時,做一些事情。我想知道是否有一種簡單的方法來獲取平均負載變數並將其乘以 100 ,或者只需將小數位向右移動兩個空格並在必要時用 0 填充,這樣就可以使這個整數算術和普通 bash (( )) 算術擴展可以接管。
現在,我將浮點數分解為截斷整數和小數作為整數(例如 1.5、LOAD1_INT=1、LOAD1_DECIMAL=50),但如果可能的話,我想簡化它。
目前(複雜)版本:
CRIT_LOAD=3.5 if [[ $CRIT_LOAD =~ ^[0-9]{1,2}\.[0-9]{1,2}$ ]]; then # Since bash can't handle floating point arithmetic, break $CRIT_LOAD float into 2 separate integers CRIT_LOAD_INT=$(echo $CRIT_LOAD | cut -d'.' -f1) CRIT_LOAD_DECIMAL=$(echo $CRIT_LOAD | cut -d'.' -f2) elif [[ $CRIT_LOAD =~ ^[0-9]{1,2}$ ]]; then # If $CRIT_LOAD is already an int, update variables so Monitor code works unchanged CRIT_LOAD_INT=$CRIT_LOAD CRIT_LOAD_DECIMAL=0 else # Set a default value of 1.0 if we can't parse CRIT_LOAD value CRIT_LOAD_INT=1 CRIT_LOAD_DECIMAL=0 fi LOAD1=$(cat /proc/loadavg | cut -d' ' -f1) LOAD1_INT=$(echo $LOAD1 | cut -d'.' -f1) LOAD1_DECIMAL=$(echo $LOAD1 | cut -d'.' -f2) # Current load int is already higher than critical threshold int if (( LOAD1_INT > CRIT_LOAD_INT )); then log "CRITICAL: Load values have exceeded threshold." elif (( LOAD1_INT == CRIT_LOAD_INT )); then # If current load int is same as crit threshold int, compare decimals if (( LOAD1_DECIMAL > CRIT_LOAD_DECIMAL )); then log "CRITICAL: Load values have exceeded threshold." fi fi
有沒有辦法通過簡單地將loadavg(例如1.50)轉換為int(例如150)來減少所有這些程式碼?同樣,不使用該
bc
實用程序,因為它在這些系統上不可用。**編輯:**我最終接受了
printf
@ilkkachu 建議的命令並將其修改為一個函式以在我的程式碼中使用。我在awk
命令上選擇了這條路線,因為在這段程式碼中還有其他地方呼叫函式來模擬浮點運算可以簡化程式碼的可讀性和可重用性。將他的答案標記為解決方案。function dec_to_int() { DECIMAL=$1 SCALE_FACTOR=$2 # printf removes decimal and allows $SCALE_FACTOR additional spaces to be included, 0-pads numbers that would be too small otherwise # NOTE: printf will round number if the values it keeps are greater than the scale factor # e.g. SCALE_FACTOR=2, 1.759 -> 176 SCALED_INT=$(printf "%.0f\n" "${DECIMAL}e${SCALE_FACTOR}") echo $SCALED_INT } LOAD1=$(cat /proc/loadavg | cut -d' ' -f1) LOAD1=$(dec_to_int $LOAD1 2)
如果您想/需要純粹在 Bash(甚至 Dash 或 Busybox)中執行此操作,您可以從如何使用 bash 或其他語言/框架進行整數和浮點計算?並濫用
printf
應該能夠解析浮點數並將其列印出來的事實。添加後綴 likee2
會將數字放大 100(只要該數字還沒有 E 後綴)。例如,這將按
a
100 放大列印,或者123
:a=1.23 printf "%.0f\n" "${a}e2"
或者,讀自
/proc/loadavg
:read avg1 avg5 avg15 rest < /proc/loadavg scaled_load=$(printf "%.0f\n" "${avg1}e2")
(或者
printf -v scaled_load "%.0f\n" "${avg1}e2"
代替 Bash 中的命令替換)話又說回來,只要
loadavg
小數點後只包含兩位數,就可以從中間去掉點,1.23
直接變成123
.連結的文章包含其他一些也可用於浮點運算的程序。即使您的安裝沒有
bc
,它也可能有例如awk
.
如果我理解您的程式碼正確,您希望在 的第一個值
/proc/loadavg
大於時記錄一條消息CRIT_LOAD
。/proc/loadavg
可以使用單個awk
命令完成包括解析在內的檢查。if ! awk -v crit_load=3.5 '$1>crit_load { exit 1 }' /proc/loadavg then log "CRITICAL: Load values have exceeded threshold." fi
腳本讀取(從
awk
所有行開始)/proc/loadavg
,如果第一個欄位大於crit_load
它以程式碼 1(錯誤,假)退出,否則以程式碼 0(OK,真)退出。如果門檻值已經在 shell 變數中,您當然可以將其用於
awk
變數分配。CRIT_LOAD=3.5 if ! awk -v crit_load="$CRIT_LOAD" '$1>crit_load { exit 1 }' /proc/loadavg then # ...
如有必要,
awk
還可以擴展腳本以檢查其他欄位/proc/loadavg
。這將比執行多個awk
命令並在 shell 腳本中組合結果更有效和更一致。如果要區分更多情況,可以更改腳本以使用不同的退出程式碼。
awk -v crit_load=3.5 -v warn_load=1.5 '$1>crit_load { exit 2 } $1>warn_load { exit 1 }' /proc/loadavg case "$?" in 2) do_something ;; 1) do_other ;; 0) do_nothing ;; *) handle_unknown_value ;; esac