Date: Tue, 19 Aug 2008 15:52:46 +0200 (CEST) From: Oliver Fromme <olli@lurza.secnetix.de> To: freebsd-questions@FreeBSD.ORG, David Wolfskill <david@catwhisker.org> Subject: Re: Shell scripts: variable assignment within read loops Message-ID: <200808191352.m7JDqkQA010206@lurza.secnetix.de> In-Reply-To: <20080818013328.GY44815@bunrab.catwhisker.org>
next in thread | previous in thread | raw e-mail | index | archive | help
David Wolfskill wrote: > I am writing a (Bourne) shell script that is intended (among other > things) to obtain information from a command, such as: > > netstat -nibd -f inet > > by reading and parsing the output. > > However, the "obvious" (to me) approach of piping the output of the > command to the standard input of a "while read ..." statement turns out > to be not very useful; > [...] > Well, that's not *quite* accurate:the assignment is done all right, but > in the latter case, it appears to be done in a subshell, so by the time > we get to the "echo" statement, any variable assignments from within the > read loop have vanished. That's correct, as Giorgos has already pointed out. Most bourne shells execute all parts of a pipe except the first one in a subshell, so any assignments are lost. A common way is to echo things from within the subshell and capture them through command expansion, like this: foo=$( something | while read x; do whatever echo "value" done ) That will assign "value" to the variable foo. This only works for single variables, of course. If you want to assign to multiple variables, it gets a little more tricky. One way is to use single assignment like above, and then split it into several variables on a delimiter character. The following will split $foo on whitespace and assign the results to $1, $2, $3 etc., with the count in $#: set -- $foo You can split on any other character by setting the IFS variable of the shell appropriately. If you know in advance how many values you'll get, another possibility is to use a so-called "here document": read foo1 foo2 foo3 rest <<end $( something | while read x; do whatever echo -n "value " done ) end For example, a simple way to get hour, minutes and seconds into three variables without having to exec date(1) three times: read H M S <<end $( date +"%H %M %S" ) end Or: set -- $( date +"%H %M %S" ) H=$1 M=$2 S=$3 It gets more complicated if you need to get the exit code of some parts of the pipe except the last one. You didn't ask for that, though. :-) Best regards Oliver -- Oliver Fromme, secnetix GmbH & Co. KG, Marktplatz 29, 85567 Grafing b. M. Handelsregister: Registergericht Muenchen, HRA 74606, Geschäftsfuehrung: secnetix Verwaltungsgesellsch. mbH, Handelsregister: Registergericht Mün- chen, HRB 125758, Geschäftsführer: Maik Bachmann, Olaf Erb, Ralf Gebhart FreeBSD-Dienstleistungen, -Produkte und mehr: http://www.secnetix.de/bsd 'Instead of asking why a piece of software is using "1970s technology," start asking why software is ignoring 30 years of accumulated wisdom.'
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200808191352.m7JDqkQA010206>