Reputation: 1177
I have a file which contains following 10 numbers:
> cat numbers
9
11
32
88
89
90
95
104
118
120
>
I would like to print out the preceding number only if it is at least 5 numbers smaller than the current number. So I expect output like this:
11
32
90
95
104
120
I have a script which does this:
> cat test.sh
#!/bin/bash
subtraction="5"
while read -r number; do
if [ -n "$previous_number" ] && (( $((number - subtraction)) >= previous_number )); then
echo "$previous_number"
fi
previous_number="$number"
done < "$1"
> ./test.sh numbers
11
32
90
95
104
>
However, it doesn't print 120
. What is the most elegant/proper solution in such cases? Should I simply add tail -1 "$1"
after the while loop?
Upvotes: 0
Views: 232
Reputation: 295403
For someone else reading this for whom while read
genuinely is not iterating over the last line of a file, there's a likely different problem: An input file without a trailing newline.
For that, one can amend their code as follows:
while read -r number || [[ $number ]]; do
: "...logic here..."
done
This is true because without a trailing newline, read
will return false, and so the body of the loop will not be executed with the original code, but $number
is still populated.
However, for this specific program and its specific input given, there's nothing at all wrong with how the while read
idiom handles the last line of an input; the output at hand follows from the program's logic as written and defined.
Consider the following version, which makes what's happening more clear:
#!/bin/bash
subtraction="5"
while read -r number; do
if [[ $previous_number ]] && (( (number - subtraction) >= previous_number )); then
printf '%q is at least %q away from %q\n' "$previous_number" "$subtraction" "$number"
else
printf '%q is not %q away from %q\n' "$previous_number" "$subtraction" "$number"
fi
previous_number="$number"
done <"$1"
Its output is:
'' is not 5 away from 9
9 is not 5 away from 11
11 is at least 5 away from 32
32 is at least 5 away from 88
88 is not 5 away from 89
89 is not 5 away from 90
90 is at least 5 away from 95
95 is at least 5 away from 104
104 is at least 5 away from 118
118 is not 5 away from 120
...as this last line of output shows, it is genuinely considering 120
, and deciding not to print it per your program's logic as defined.
Upvotes: 2
Reputation: 785128
It is easier to use awk
for this job:
awk 'NR>1 && $1-p>=5{print p} {p=$1}' file
Output:
11
32
90
95
104
btw 120
won't be printed in output because preceding number is 118
which is not <=5
to 120
.
Upvotes: 2