I can iterate over the lines of a file this way:
while read l; do echo $l; done < file
Is there a way to iterate over only the top 5 lines?
I can iterate over the lines of a file this way:
while read l; do echo $l; done < file
Is there a way to iterate over only the top 5 lines?
Just do something like:
n=5
while IFS= read -ru3 line && (( n-- )); do
printf 'Got this line: "%s"\n' "$line"
done 3< some-file
Though, here, if it's about text processing, best would probably be to use a text processing tool:
LC_ALL=C sed 's/.*/Got this line: "&"/;5q' < some-file
Or:
awk '{print "Got this line: \""$0"\""}; NR == 5 {exit}' < some-file
Or:
perl -lne 'print qq(Got this line: "$_"); last if $. == 5' < some-file
Related:
There are many ways to iterate over the first five lines of a file; here are a few. Note that the last ones are the most efficient and are generally a better way to approach this kind of problem than using a shell script loop.
Brute force:
{
OIFS="$IFS" IFS=
read -r line && printf "%s\n" "$line"
read -r line && printf "%s\n" "$line"
read -r line && printf "%s\n" "$line"
read -r line && printf "%s\n" "$line"
read -r line && printf "%s\n" "$line"
IFS="$OIFS"
} <file
A loop:
for ((i=1; i<=5; i++))
do
IFS= read -r line
printf "%s\n" "$line"
done <file
Another loop, suitable for small ranges as the expression is expanded before evaluation into a list of all its values (i.e. {1..5}
is converted to 1 2 3 4 5
before execution):
for i in {1..5}
do
IFS= read -r line
printf "%s\n" "$line"
done <file
Considering just the beginning of the file, but beware that any variables set in this loop will not be accessible outside of it
head -n5 file |
while IFS= read -r line
do
printf "%s\n" "$line"
done
Not using a loop at all
head -n5 file
sed 5q file
for i in {1..5}
instead of for ((i=1; i<=5; i++))
. Just to learn, that's why I'm asking.
– schrodingerscatcuriosity
May 31 '22 at 17:12
for i in 1 2 3 4 5
and wouldn't necessarily be scalable to (say) {1..1000000}
, whereas mine represents i=1; while (( i <= 5 )) do ... ((i++)); done
and should scale to any range.
– Chris Davies
May 31 '22 at 17:37
for
loop, you only store the loop variable in memory. Traditional for
loops (the first kind) always iterates over a static list stored in memory. See also bash counting script, counts fine ascending, doing error by counting descending
– Kusalananda
May 31 '22 at 17:39
You can use process substitution, to redirect from the output of head -n 5 file
rather than just file
. Unlike using a pipe from head -n 5 file
, with process substitution, the while loop runs in the current shell and is able to set/change variables in that shell and otherwise affect its environment - a child process or subshell, e.g. a pipe, is not able to affect its parent's environment.
For example:
while read l; do printf '%s\n' "$l"; done < <(head -n 5 file)
I'd include an explanation of why I'm using printf
instead of echo
and a warning about not using shell to process text, but Stéphane's answer has already done that. I recommend that you read the links in that answer.
head -5 file | while read l; do echo $l; done
– Artem S. Tashkinov May 31 '22 at 16:34head -5
, but see https://unix.stackexchange.com/q/9954/170373 – ilkkachu May 31 '22 at 16:35n=5; i=0; while read l; do echo $l; i=$(($i+1)); if [ $i -ge $n ]; then break; fi; done < file
– Jim L. May 31 '22 at 17:07IFS= read -r l
to keep the data intact, see https://unix.stackexchange.com/q/169716/170373 and even then you may get problems if the file is missing the trailing newline, see https://unix.stackexchange.com/questions/478720/what-does-while-read-r-line-n-line-mean) – ilkkachu May 31 '22 at 17:43