I'm needing to write a script on some embedded Linux systems that don't have bc
installed on them and won't be able to have it installed. The script I'm writing is essentially a monitoring script that takes certain corrective actions at different load values. For example, at load average 1.5, do something.
I am wondering if there's a simple way to take the load average variable and just multiply it by 100 or simply move the decimal place to the right two spaces and pad with 0s if necessary, that would then make this integer arithmetic and normal bash (()) arithmetic expansion could take over.
Right now, I'm breaking the floating point number its truncated integer and the decimal as a whole number(e.g. 1.5, LOAD1_INT=1, LOAD1_DECIMAL=50) but would like to simplify it if possible.
Current (complicated) version:
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
Is there a way to just pare down all this code by simply turning loadavg (e.g. 1.50) into an int (e.g. 150)? Again, without using the bc
utility as it't not available on these systems.
EDIT: I wound up taking the printf
command @ilkkachu suggested and modified it into a function for use in my code. I chose this route over the awk
command as there are other places in this code where calling a function to emulate floating point arithmetic simplifies code readability and re-usability. Marking his answer as solution.
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)
^(3\.[6-9]|[4-9]\.|[0-9]{2,}\.)
? Or something similar.bc
installed out of the box. See alsopax
andm4
which are often omitted.awk
installed :D3.5
and3.25
, you might get an unexpected result if you compare5
with25
.