Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does unix while read not read last line? [duplicate]

Tags:

My script

export IFS=":"

cat hello | while read a b c d; do
    echo $a,$b,$c,$d
done

My file hello

1:1:1:1
2:2:2:2
3:3:3:3

My output

1,1,1,1
2,2,2,2

If I put a blank line after 3:3:3:3 in hello then the output becomes

1,1,1,1
2,2,2,2
3,3,3,3

Anyone know how I can fix this problem so I don't need to put a blank line at the end of hello?

like image 879
Popcorn Avatar asked Nov 15 '13 21:11

Popcorn


People also ask

How do I read the last line of a file in Shell?

To look at the last few lines of a file, use the tail command. tail works the same way as head: type tail and the filename to see the last 10 lines of that file, or type tail -number filename to see the last number lines of the file.

How do I print the last row in Unix?

tail [OPTION]... [ Tail is a command which prints the last few number of lines (10 lines by default) of a certain file, then terminates. Example 1: By default “tail” prints the last 10 lines of a file, then exits.

How do I get the last line in bash?

Then, the first line would be ${lines[0]} , and the last line would be ${lines[-1]} . In older versions of bash , negative indices aren't allowed and you'll have to compute the last index manually: ${lines[${#lines[@]}-1]} .


2 Answers

What's happening is, the read command fails when the input is not terminated with a newline. Since the newline character is missing at the end of your file, the read fails, so the last iteration of the while loop is skipped.

If you don't want to / cannot make sure that your input file has a newline at the end, you can group your cat with an echo to give the appearance of an input terminated by newline, for example like this:

{ cat hello; echo; } | while read a b c d; do
    echo $a,$b,$c,$d
done

or like this:

(cat hello; echo) | while read a b c d; do
    echo $a,$b,$c,$d
done
like image 157
janos Avatar answered Oct 20 '22 08:10

janos


This is an extensive reading for the question why?

If you don't want to put a new line at the end, you can do this:

while IFS=: read -r a b c d || [ -n "$a" ]; 
do     
echo $a,$b,$c,$d; 
done < file

Or using grep:

while IFS=: read -r a b c d; 
do     
echo $a,$b,$c,$d; 
done < <(grep "" filee)

Note:

  1. There's no need to use cat with while loop.
  2. You should almost always use the -r option with read.
like image 40
Jahid Avatar answered Oct 20 '22 06:10

Jahid