Date: Sun, 17 Aug 2008 18:33:28 -0700 From: David Wolfskill <david@catwhisker.org> To: freebsd-questions@freebsd.org Subject: Shell scripts: variable assignment within read loops Message-ID: <20080818013328.GY44815@bunrab.catwhisker.org>
next in thread | raw e-mail | index | archive | help
--rLeWyzpnKP7Xancb Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable 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; it appears that while foo=3D"" while read bar ... ; do ... foo=3D$bar ... done <$filename echo $foo will assign to foo the value of the bar variable form the last record read (in FreeBSD 6.3-STABLE, at least), the following fails to do so: foo=3D"" cat $filename | while read bar ... ; do ... foo=3D$bar ... done echo $foo 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. Here's a cut/pasted, somewhat contrived example: #! /bin/sh foo=3D0 echo "0 foo: $foo" while read line; do echo "1.0 foo: $foo" foo=3D1$line echo "1.1 foo: $foo" done </etc/resolv.conf echo "2 foo: $foo" echo "" foo=3D2 echo "3 foo: $foo" eval "cat /etc/resolv.conf | grep '.'" cat /etc/resolv.conf | while read line; do echo "4.0 foo: $foo" foo=3D3$line echo "4.1 foo: $foo" done echo "5 foo: $foo" exit 0 and its output on my laptop: 0 foo: 0 1.0 foo: 0 1.1 foo: 1search catwhisker.org 1.0 foo: 1search catwhisker.org 1.1 foo: 1nameserver 172.16.8.12 1.0 foo: 1nameserver 172.16.8.12 1.1 foo: 1nameserver 172.16.8.11 1.0 foo: 1nameserver 172.16.8.11 1.1 foo: 1nameserver 172.16.8.1 2 foo: 1nameserver 172.16.8.1 3 foo: 2 search catwhisker.org nameserver 172.16.8.12 nameserver 172.16.8.11 nameserver 172.16.8.1 4.0 foo: 2 4.1 foo: 3search catwhisker.org 4.0 foo: 3search catwhisker.org 4.1 foo: 3nameserver 172.16.8.12 4.0 foo: 3nameserver 172.16.8.12 4.1 foo: 3nameserver 172.16.8.11 4.0 foo: 3nameserver 172.16.8.11 4.1 foo: 3nameserver 172.16.8.1 5 foo: 2 Note that the "2 foo" line shows a value read from the last line of /etc/resolv.conf, while the "5 foo" line fails to do so. (In a Solaris 9 environment, the output from each stanza is the same as the output from teh second stanza in FreeBSD.) Now here's a copy of the in-development script: #! /bin/sh cmd=3D"netstat -nibd -f inet" ctr=3D0 clist=3D"" hlist=3D`$cmd | head -1` for f in $hlist; do ctr=3D$(( $ctr + 1 )) eval c$ctr=3D\"$f\" eval h_$f=3Dc$ctr done cmax=3D$ctr t_file=3D`mktemp /tmp/XXXXXXXXX` $cmd | tail +2 >$t_file while read $hlist dummy; do if [ "$Name" =3D "lo0" ]; then continue fi for f in $hlist; do eval val=3D\"\$$f\" case $val in -) eval ${f}_$Name=3D0;; *) eval ${f}_$Name=3D"$val";; esac; done nics=3D"$Name $nics"; done</$t_file rm $t_file echo "(end) NICs: $nics" for n in $nics; do for f in $hlist; do eval "echo ${f}_$n: \$${f}_$n" done done exit 0 And its output on my laptop: (end) NICs: ath0=20 Name_ath0: ath0 Mtu_ath0: 1500 Network_ath0: 172.17 Address_ath0: 172.17.1.30 Ipkts_ath0: 725191 Ierrs_ath0: 0 Ibytes_ath0: 185144197 Opkts_ath0: 821917 Oerrs_ath0: 0 Obytes_ath0: 74260936 Coll_ath0: 0 Drop_ath0: 0 and (somewhat more interestingly) on my "firewall" machine: (end) NICs: dc0 de0 fxp0=20 Name_dc0: dc0 Mtu_dc0: 1500 Network_dc0: 172.16.8/24 Address_dc0: 172.16.8.1 Ipkts_dc0: 2501577 Ierrs_dc0: 0 Ibytes_dc0: 215386153 Opkts_dc0: 20269087 Oerrs_dc0: 0 Obytes_dc0: 2553930555 Coll_dc0: 0 Drop_dc0: 0 Name_de0: de0 Mtu_de0: 1500 Network_de0: 63.193.123/24 Address_de0: 63.193.123.122 Ipkts_de0: 5936847 Ierrs_de0: 0 Ibytes_de0: 734092787 Opkts_de0: 18557543 Oerrs_de0: 0 Obytes_de0: 2551089632 Coll_de0: 0 Drop_de0: 0 Name_fxp0: fxp0 Mtu_fxp0: 1500 Network_fxp0: 172.17 Address_fxp0: 172.17.0.1 Ipkts_fxp0: 10013 Ierrs_fxp0: 0 Ibytes_fxp0: 1366082 Opkts_fxp0: 1253115 Oerrs_fxp0: 0 Obytes_fxp0: 70429903 Coll_fxp0: 0 Drop_fxp0: 0 As you see, I am circumventing the issue by writing to a transient file. In the intended application, the script is to be used to gather resource-utilization information; thus, I want its "footprint" to be smaller, rather than larger. Granted, in my case, I would be writing a tiny text file to a swap-backed tmpfs, but in production, I won't have the luxury of knowing that in advance: the intent is that the script must run on a minimal FreeBSD system, with no "ports" or other 3rd-party software installed. Is there some other -- possibly better -- way to do this (using Bourne shell scripting)? Thanks! (Please select recipients for your replies carefully: I'm not subscribed to -questions. I've provided a hint in the form of a Reply-To header, though I realize that not all mailers honor it. Please do include me in replies.) Peace, david --=20 David H. Wolfskill david@catwhisker.org Depriving a girl or boy of an opportunity for education is evil. See http://www.catwhisker.org/~david/publickey.gpg for my public key. --rLeWyzpnKP7Xancb Content-Type: application/pgp-signature Content-Disposition: inline -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.0 (FreeBSD) iEYEARECAAYFAkio0WYACgkQmprOCmdXAD25IACghD6fZO0NBpw6LBwpLsBslVuy x4gAoIG5Agc5cpSClYEz0LUB8PcUTz+F =iF8V -----END PGP SIGNATURE----- --rLeWyzpnKP7Xancb--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20080818013328.GY44815>