I have bash script that prints me table: timestamp, value1, value2, and ratio between value1/value2. For example:
2022-08-07 21:00 2784000/8750=318
2022-08-07 20:00 2792000/8760=318
2022-08-07 19:00 2790000/8760=318
2022-08-07 18:00 2776000/8750=317
2022-08-07 17:00 2804000/8570=327
2022-08-07 16:00 2799000/8580=326
2022-08-07 15:00 2783000/8590=323
2022-08-07 14:00 2729000/8580=318
2022-08-07 13:00 2638000/8530=309
2022-08-07 12:00 2637000/8490=310
2022-08-07 11:00 2610000/8450=308
2022-08-07 10:00 2595000/8470=306
2022-08-07 09:00 2616000/8490=308
2022-08-07 08:00 2604000/8470=307
2022-08-07 07:00 2619000/8450=309
2022-08-07 06:00 2626000/8530=307
2022-08-07 05:00 2627000/8530=307
2022-08-07 04:00 2634000/8530=308
2022-08-07 03:00 2626000/8470=310
2022-08-07 02:00 2625000/8530=307
2022-08-07 01:00 2676000/8540=313
2022-08-07 00:00 2654000/8590=308
2022-08-06 23:00 2673000/8610=310
2022-08-06 22:00 2641000/8570=308
2022-08-06 21:00 2655000/8610=308
2022-08-06 20:00 2665000/8670=307
2022-08-06 19:00 2672000/8670=308
2022-08-06 18:00 2626000/8680=302
2022-08-06 17:00 2581000/8690=297
2022-08-06 16:00 2581000/8720=295
2022-08-06 15:00 2560000/8720=293
Now after sorting it basing on value on right side of '=' ./script.sh | cut -f2,2 -d= | sort -n
. Now I want to assign max and min. Ofc its easy to assign max as output of last line, and min as 1st line but assigning 2 variables leads to 2 executions of script.sh
. Is there any smarter way to fetch max and min without printing output 2 times?
CodePudding user response:
Use any command that prints only the first and last line (e.g. sed -n '1p;$p'
) then read both lines into an array using mapfile
, or put both lines into a single one with a separator that does not appear in the input, and read
two variables.
IFS=' ' read -r min max < <(./script.sh |
cut -f2,2 -d= | sort | sed -n '1p;$p' | tr '\n' ' ')
That would be one way to do it. But as Ted Lyngmo commented: There is most likely a more efficient way depending on the content of script.sh
.
Even now, we could do without the sort
(probably the costliest part of your pipeline) when using a custom script that retrieves the min and max in a single unsorted pass, e.g. something like ...
IFS=' ' read -r min max < <(./script.sh |
awk -F '=' -v min=999999 -v max=-999999 '
$2<min {min=$2} $2>max{max=$2} END {print min, max}')
CodePudding user response:
Instead of adding another subprocess call we can replace the current cut/sort
with a single awk
call to extract min/max
:
./script.sh | awk -F= '
FNR==1 { min=max=$2 }
{ min=($2<min ? $2 : min)
max=($2>max ? $2 : max)
}
END { print min,max }'
This generates:
293 327
Feeding to read
to populate the bash
variables:
read -r min max < <(./script.sh | awk -F= '
FNR==1 { min=max=$2 }
{ min=($2<min ? $2 : min)
max=($2>max ? $2 : max)
}
END { print min,max }')
### or collapsing into a single line:
read -r min max < <(./script.sh | awk -F= 'FNR==1{min=max=$2}{min=($2<min?$2:min);max=($2>max?$2:max)}END{print min,max}')
Both of these generate:
$ typeset -p min max
declare -- min="293"
declare -- max="327"
NOTE: if the purpose of script.sh
is to generate some output to be parsed for min/max
then a better approach may be to modify script.sh
to just generate the min/max
; on the other hand if script.sh
output is to be used for multiple purposes then consider redirecting stdout to a file and then parsing said file as needed