[opensuse] BASH - while read; howto read last line in file without blank line at end?
Listmates, I'm stumped on another simple BASH problem. How do I read the last line of a file with a while loop without requiring a blank line at the end of the file? Simple example I'm stuck on: { while read alias url; do echo -e "${alias}\t${url}" done } < ~/linux/scripts/config/repos The repos flat text file (shown with --- above and below to show no blank line at the end) is: --- <snip: other repos> database http://download.opensuse.org/repositories/server:/database/openSUSE_10.3/ oss http://download.opensuse.org/repositories/openSUSE:10.3/standard/ oss-src http://download.opensuse.org/distribution/10.3/repo/src-oss/suse/ lastline temp-last_line_test --- If there is no blank line at the end of the file, the while loop reads the "oss-src" line, but does not read the "lastline". If there is a blank line at the end, it reads "lastline" just fine. What is the trick to be able to read the last line of the repos file regardless of whether there is a blank at the end or not? -- David C. Rankin, J.D., P.E. Rankin Law Firm, PLLC 510 Ochiltree Street Nacogdoches, Texas 75961 Telephone: (936) 715-9333 Facsimile: (936) 715-9339 www.rankinlawfirm.com -- To unsubscribe, e-mail: opensuse+unsubscribe@opensuse.org For additional commands, e-mail: opensuse+help@opensuse.org
David C. Rankin wrote:
Listmates,
I'm stumped on another simple BASH problem. How do I read the last line of a file with a while loop without requiring a blank line at the end of the file? Simple example I'm stuck on:
{ while read alias url; do echo -e "${alias}\t${url}" done } < ~/linux/scripts/config/repos
Remove the curly brackets and everything runs fine on my system. -- Sandy List replies only please! Please address PMs to: news-reply2 (@) japantest (.) homelinux (.) com -- To unsubscribe, e-mail: opensuse+unsubscribe@opensuse.org For additional commands, e-mail: opensuse+help@opensuse.org
On Sat, 1 Nov 2008, Sandy Drobic wrote:-
David C. Rankin wrote:
Listmates,
I'm stumped on another simple BASH problem. How do I read the last line of a file with a while loop without requiring a blank line at the end of the file?
You can't, not if the loop-test is using the true/false return from read to work out whether to execute the loop body or not. Without an EOL, read will actually read the line and store it in the variables, but returns a false if there's no EOL which the 'while' then sees as a failure and so causes it to skip the body of the loop.
Simple example I'm stuck on:
{ while read alias url; do echo -e "${alias}\t${url}" done } < ~/linux/scripts/config/repos
Remove the curly brackets and everything runs fine on my system.
Not here it doesn't, or at least not on 10.3 or 11.0. If you've used an editor to create the file, it's possible that it's auto-added an EOL to the last line. I know from experience that both nano and pico do, whereas Kate/Kwrite don't. Anyway, you can see the effect yourself by using printf, without using a terminating '\n', to ensure there's no final EOL character. Something like this gives an ample demonstration of the problem: davjam@playing:~> printf "line_one\thas_EOL\nline_two\thas_EOL\nline_three\thas_EOL\nline_four\tno_EOL" line_one has_EOL line_two has_EOL line_three has_EOL line_four no_EOLdavjam@playing:~> ^^^^^^^^^^^^^^^^^ Shows there was no EOL character or this would have been on a new line. Next, using the same printf and piping it into a loop: davjam@playing:~> printf "line_one\thas_EOL\nline_two\thas_EOL\nline_three\thas_EOL\nline_four\tno_EOL" | \
while read LINE TEXT ; do echo "${LINE} -> ${TEXT}" ; done line_one -> has_EOL line_two -> has_EOL line_three -> has_EOL davjam@playing:~>
Nope, it's missing the last line. Just to make sure, lets try sending the output of printf into a file and then using that: davjam@playing:~> printf "line_one\thas_EOL\nline_two\thas_EOL\nline_three\thas_EOL\nline_four\tno_EOL" >dummy.txt davjam@playing:~> while read LINE TEXT ; do echo "${LINE} -> ${TEXT}" ; done <dummy.txt line_one -> has_EOL line_two -> has_EOL line_three -> has_EOL davjam@playing:~> Okay, it's still missing the last line. Last try, and I've split this one up so it's spread over a few lines to make it easier to read, with one that will work and should even handle 0-length files: davjam@playing:~> while :
do read LINE TEXT EXIT="$?" [ -n "${LINE}${TEXT}" ] && echo "${LINE} -> ${TEXT}" [ "${EXIT}" -ne 0 ] && break done <dummy.txt line_one -> has_EOL line_two -> has_EOL line_three -> has_EOL line_four -> no_EOL davjam@playing:~>
With this one, the echo command is executed only if ${LINE} and/or ${TEXT} have any contents. I assigned the return value from the read command and use that after the echo command to decide whether to break out of the loop. This avoids printing out: -> if there's a blank line in the input file. Of course, you could add another line: [ -z "${LINE}${TEXT}" ] && echo to show there was a blank line present. Regards, David Bolt -- Team Acorn: http://www.distributed.net/ RC5-72 @ ~70Mkeys/s SUSE 10.1 32 | | openSUSE 10.3 32b | openSUSE 11.0 32b | openSUSE 10.2 64b | openSUSE 10.3 64b | openSUSE 11.0 64b RISC OS 3.6 | TOS 4.02 | openSUSE 10.3 PPC | RISC OS 3.11 -- To unsubscribe, e-mail: opensuse+unsubscribe@opensuse.org For additional commands, e-mail: opensuse+help@opensuse.org
David Bolt wrote:
On Sat, 1 Nov 2008, Sandy Drobic wrote:-
David C. Rankin wrote:
Listmates,
I'm stumped on another simple BASH problem. How do I read the last line of a file with a while loop without requiring a blank line at the end of the file?
You can't, not if the loop-test is using the true/false return from read to work out whether to execute the loop body or not. Without an EOL, read will actually read the line and store it in the variables, but returns a false if there's no EOL which the 'while' then sees as a failure and so causes it to skip the body of the loop.
Okay, it's still missing the last line. Last try, and I've split this one up so it's spread over a few lines to make it easier to read, with one that will work and should even handle 0-length files:
davjam@playing:~> while :
do read LINE TEXT EXIT="$?" [ -n "${LINE}${TEXT}" ] && echo "${LINE} -> ${TEXT}" [ "${EXIT}" -ne 0 ] && break done <dummy.txt line_one -> has_EOL line_two -> has_EOL line_three -> has_EOL line_four -> no_EOL davjam@playing:~>
With this one, the echo command is executed only if ${LINE} and/or ${TEXT} have any contents. I assigned the return value from the read command and use that after the echo command to decide whether to break out of the loop. This avoids printing out:
->
if there's a blank line in the input file.
Of course, you could add another line:
[ -z "${LINE}${TEXT}" ] && echo
to show there was a blank line present.
Regards, David Bolt
That was the trick I was looking for! Are you sure someone didn't install a BASH processor inside you head when you were asleep! Thanks David. -- David C. Rankin, J.D., P.E. Rankin Law Firm, PLLC 510 Ochiltree Street Nacogdoches, Texas 75961 Telephone: (936) 715-9333 Facsimile: (936) 715-9339 www.rankinlawfirm.com -- To unsubscribe, e-mail: opensuse+unsubscribe@opensuse.org For additional commands, e-mail: opensuse+help@opensuse.org
On Tue, 4 Nov 2008, David C. Rankin wrote:-
That was the trick I was looking for! Are you sure someone didn't install a BASH processor inside you head when you were asleep! Thanks David.
Not that I know of. I just had a quick look on Google using the search terms "bash reading text no eol". The second link was to a thread in the comp.unix.shell newsgroup where this situation was described, and the reason for the behaviour is given. I just used that to create my own possible solution. Regards, David Bolt -- Team Acorn: http://www.distributed.net/ OGR-NG @ ~100Mnodes RC5-72 @ ~1Mkeys/s SUSE 10.1 32 | | openSUSE 10.3 32b | openSUSE 11.0 32b | openSUSE 10.2 64b | openSUSE 10.3 64b | openSUSE 11.0 64b RISC OS 3.6 | TOS 4.02 | openSUSE 10.3 PPC | RISC OS 3.11 -- To unsubscribe, e-mail: opensuse+unsubscribe@opensuse.org For additional commands, e-mail: opensuse+help@opensuse.org
That was the trick I was looking for! Are you sure someone didn't install a BASH processor inside you head when you were asleep! Thanks David.
God I hope not, since I consider ksh vastly superior to bash and have been lovingly feeding, watering, and singing lulabys to my ksh processor for years. :) Brian K. White brian@aljex.com http://www.myspace.com/KEYofR +++++[>+++[>+++++>+++++++<<-]<-]>>+.>.+++++.+++++++.-.[>+<---]>++. filePro BBx Linux SCO FreeBSD #callahans Satriani Filk! -- To unsubscribe, e-mail: opensuse+unsubscribe@opensuse.org For additional commands, e-mail: opensuse+help@opensuse.org
On Friday 31 October 2008 20:51, David C. Rankin wrote:
Listmates,
I'm stumped on another simple BASH problem. How do I read the last line of a file with a while loop without requiring a blank line at the end of the file? Simple example I'm stuck on:
The "head" and "tail" commands may be of interest to you.
...
-- David C. Rankin
Randall Schulz -- To unsubscribe, e-mail: opensuse+unsubscribe@opensuse.org For additional commands, e-mail: opensuse+help@opensuse.org
On Saturday 01 November 2008 04:51:43 David C. Rankin wrote:
If there is no blank line at the end of the file, the while loop reads the "oss-src" line, but does not read the "lastline". If there is a blank line at the end, it reads "lastline" just fine. What is the trick to be able to read the last line of the repos file regardless of whether there is a blank at the end or not?
Works just fine here on 11.0, with or without {} Anders -- To unsubscribe, e-mail: opensuse+unsubscribe@opensuse.org For additional commands, e-mail: opensuse+help@opensuse.org
Anders Johansson wrote:
On Saturday 01 November 2008 04:51:43 David C. Rankin wrote:
If there is no blank line at the end of the file, the while loop reads the "oss-src" line, but does not read the "lastline". If there is a blank line at the end, it reads "lastline" just fine. What is the trick to be able to read the last line of the repos file regardless of whether there is a blank at the end or not?
Works just fine here on 11.0, with or without {}
Anders
Anders I removed the curly braces and I'm still not getting the last line. the full snipped and text file are: while read alias url; do echo -e "${alias}\t${url}" done < ~/linux/scripts/config/repos echo -e "\n\t${red}Done!${nc}\n" exit 0 The text file is: http://www.3111skyline.com/download/error/repos If you run that code on that file you will not get the "lastline" information printed without modifying the script somehow. A hexdump of the file is at: http://www.3111skyline.com/download/error/repos_hex If you are running that code on those text files and you are getting the echo of "lastline", then I want to see your code... ;-) -- David C. Rankin, J.D., P.E. Rankin Law Firm, PLLC 510 Ochiltree Street Nacogdoches, Texas 75961 Telephone: (936) 715-9333 Facsimile: (936) 715-9339 www.rankinlawfirm.com -- To unsubscribe, e-mail: opensuse+unsubscribe@opensuse.org For additional commands, e-mail: opensuse+help@opensuse.org
On Friday 31 October 2008 20:51, David C. Rankin wrote:
Listmates,
I'm stumped on another simple BASH problem. How do I read the last line of a file with a while loop without requiring a blank line at the end of the file? Simple example I'm stuck on:
{ while read alias url; do echo -e "${alias}\t${url}" done } < ~/linux/scripts/config/repos
The repos flat text file (shown with --- above and below to show no blank line at the end) is:
--- [sample data elided] ---
If there is no blank line at the end of the file, the while loop reads the "oss-src" line, but does not read the "lastline". If there is a blank line at the end, it reads "lastline" just fine. What is the trick to be able to read the last line of the repos file regardless of whether there is a blank at the end or not?
I can reproduce your result if the the last line (non-blank) has no trailing newline. Specifically, if the "lastline" line you show does not have a newline, then the read loop ignores it. I guess it's a BASH oddity. Vim will warn you when you open such a file. I'm not sure if it will allow you to create one (I used a different editor to create the test data I used). Also, in this particular case, at least, none of the curly braces (inside the quoted string or around the while loop) are necessary.
-- David C. Rankin
Randall Schulz -- To unsubscribe, e-mail: opensuse+unsubscribe@opensuse.org For additional commands, e-mail: opensuse+help@opensuse.org
On Sat, 1 Nov 2008, Randall R Schulz wrote:- <snip>
I can reproduce your result if the the last line (non-blank) has no trailing newline.
That was how I read the source file to be formatted, no EOL (newline) character on the last line.
Specifically, if the "lastline" line you show does not have a newline, then the read loop ignores it.
The problem is caused by the return when read doesn't find the EOL. In this case, read will read the line and puts the value(s) into the right variable(s) but it then returns false as the response code. Since while is looking for a non-zero response from read to decide whether to execute the contents of the loop, or not, this causes it to skip the loop and so you don't see the output from the last read.
I guess it's a BASH oddity. Vim will warn you when you open such a file. I'm not sure if it will allow you to create one (I used a different editor to create the test data I used).
I used printf and didn't add a final '\n' for my test data. That ensured no final EOL was added.
Also, in this particular case, at least, none of the curly braces (inside the quoted string or around the while loop) are necessary.
They may not be required, but always including them is still a useful habit to have as it ensures you're going to be using them when you actually need to use them. Regards, David Bolt -- Team Acorn: http://www.distributed.net/ OGR-NG @ ~100Mnodes RC5-72 @ ~15Mkeys/s SUSE 10.1 32 | | openSUSE 10.3 32b | openSUSE 11.0 32b | openSUSE 10.2 64b | openSUSE 10.3 64b | openSUSE 11.0 64b RISC OS 3.6 | TOS 4.02 | openSUSE 10.3 PPC | RISC OS 3.11 -- To unsubscribe, e-mail: opensuse+unsubscribe@opensuse.org For additional commands, e-mail: opensuse+help@opensuse.org
On Saturday 01 November 2008 07:23, David Bolt wrote:
On Sat, 1 Nov 2008, Randall R Schulz wrote:-
...
Also, in this particular case, at least, none of the curly braces (inside the quoted string or around the while loop) are necessary.
They may not be required, but always including them is still a useful habit to have as it ensures you're going to be using them when you actually need to use them.
That's a matter of philosophy, I guess. As a programmer, I'm a minimalist. I eschew "just in case" habits like extra punctuation. It's better to think about every clause of every statement you write that it is to do programming on auto-pilot.
Regards, David Bolt
Randall Schulz -- To unsubscribe, e-mail: opensuse+unsubscribe@opensuse.org For additional commands, e-mail: opensuse+help@opensuse.org
----- Original Message ----- From: "David C. Rankin" <drankinatty@suddenlinkmail.com> To: "suse" <opensuse@opensuse.org> Sent: Friday, October 31, 2008 10:51 PM Subject: [opensuse] BASH - while read; howto read last line in file without blank line at end?
Listmates,
I'm stumped on another simple BASH problem. How do I read the last line of a file with a while loop without requiring a blank line at the end of the file? Simple example I'm stuck on:
{ while read alias url; do echo -e "${alias}\t${url}" done } < ~/linux/scripts/config/repos
The repos flat text file (shown with --- above and below to show no blank line at the end) is:
When read reaches end-of-file instead of end-of-line, it does read in the data and assign it to the variables, but it exits with a non-zero status. If your loop is constructed "while read ;do stuff ;done <data" then the while command sees the non-zero exit and does not do stuff, exactly as requested. So instead of testing the read exit status directly, test a flag, and have the read command set that flag from within the loop body. That way regardless of reads exit status, the entire loop body runs, because read was just one of the list of commands in the loop like any other, not a deciding factor of if the loop will get run at all. DONE=false until $DONE ;do read || DONE=true # process $REPLY here done < /path/to/file.in -- Brian K. White brian@aljex.com http://www.myspace.com/KEYofR +++++[>+++[>+++++>+++++++<<-]<-]>>+.>.+++++.+++++++.-.[>+<---]>++. filePro BBx Linux SCO FreeBSD #callahans Satriani Filk! -- To unsubscribe, e-mail: opensuse+unsubscribe@opensuse.org For additional commands, e-mail: opensuse+help@opensuse.org
Brian K. White wrote:
----- Original Message ----- From: "David C. Rankin" <drankinatty@suddenlinkmail.com> To: "suse" <opensuse@opensuse.org> Sent: Friday, October 31, 2008 10:51 PM Subject: [opensuse] BASH - while read; howto read last line in file without blank line at end?
Listmates,
I'm stumped on another simple BASH problem. How do I read the last line of a file with a while loop without requiring a blank line at the end of the file? Simple example I'm stuck on:
{ while read alias url; do echo -e "${alias}\t${url}" done } < ~/linux/scripts/config/repos
The repos flat text file (shown with --- above and below to show no blank line at the end) is:
When read reaches end-of-file instead of end-of-line, it does read in the data and assign it to the variables, but it exits with a non-zero status. If your loop is constructed "while read ;do stuff ;done <data" then the while command sees the non-zero exit and does not do stuff, exactly as requested.
So instead of testing the read exit status directly, test a flag, and have the read command set that flag from within the loop body. That way regardless of reads exit status, the entire loop body runs, because read was just one of the list of commands in the loop like any other, not a deciding factor of if the loop will get run at all.
DONE=false until $DONE ;do read || DONE=true # process $REPLY here done < /path/to/file.in
Thanks Brian - I see light at the end of this tunnel..... -- David C. Rankin, J.D., P.E. Rankin Law Firm, PLLC 510 Ochiltree Street Nacogdoches, Texas 75961 Telephone: (936) 715-9333 Facsimile: (936) 715-9339 www.rankinlawfirm.com -- To unsubscribe, e-mail: opensuse+unsubscribe@opensuse.org For additional commands, e-mail: opensuse+help@opensuse.org
participants (6)
-
Anders Johansson
-
Brian K. White
-
David Bolt
-
David C. Rankin
-
Randall R Schulz
-
Sandy Drobic