[buug] loops and pipe sinks are subshells, aaaargh
Michael Paoli
Michael.Paoli at cal.berkeley.edu
Fri Aug 5 23:28:04 PDT 2011
> From: "Michael Paoli" <Michael.Paoli at cal.berkeley.edu>
> Subject: Re: [buug] loops and pipe sinks are subshells, aaaargh
> Date: Fri, 05 Aug 2011 14:30:08 -0700
> Ian also mentioned case/example at meeting yesterday, that's less
> trivial, e.g. one wants to count for not just one string/item, but say
> several items. I may write up an example solution for that (have it in
> my head ... just have to type, test, post ... all that requires is ...
> time - of which I usually seem to have too little). I'll drop a hint
> on how that might be approached (and without temporary files): eval
A few more examples.
First a wee bit of a note - bash manages to screw this up and not work -
I wasted probably about 20 minutes trying to figure out what "I" did
wrong, ... when I had not done anything wrong as far as I'm aware.
Seems to be a bug even in relatively current version of bash, and also
significantly older version - and probably many other versions (present
in at least versions:
2.05b.0(1)-release
and:
3.00.15(1)-release
... likely also those between and probably before, and at least somewhat
after
) and bash messes it up even when invoked with arg0 of sh or /bin/sh or
the like, in which case it should behave in POSIXly correct manner.
The ash and dash shells seem to handle it just fine, ... and - with
minor adjustments for historical backwards compatibility, even ye old
Bourne shell from Unix 7th edition (circa 1979) handles it just fine.
So, first, we have:
$ expand -t 4 < eval
#!/bin/sh
output_foo(){
cat <<- \__EOT__
foo 1
bar 23
baz 4
foo 53
foobar 6
whatever 99
...
__EOT__
}
eval $(
foo=0
bar=0
baz=0
foobar=0
output_foo |
(
while read item count; do
case "$item" in
foo|bar|baz|foobar)
eval $item=\$\(expr \$$item + \$count\)
;;
esac
done
echo foo=$foo bar=$bar baz=$baz foobar=$foobar
)
)
for item in foo bar baz foobar
do
eval echo count for \$item is \$$item
done
For ye olde ancient Unix 7th edition circa 1979 Bourne shell (run under
emulation), we additionally have:
$ diff eval eval.old | expand -t 4
1c1
< #!/bin/sh
---
> :
3,15c3
< output_foo(){
< cat <<- \__EOT__
< foo 1
< bar 23
< baz 4
< foo 53
< foobar 6
< whatever 99
< ...
< __EOT__
< }
<
< eval $(
---
> eval `
20c8
< output_foo |
---
> ./output_foo |
25c13
< eval $item=\$\(expr \$$item + \$count\)
---
> eval $item=\\\`expr \\\$$item + $count\\\`
31c19
< )
---
> `
$ cat output_foo
:
cat << \__EOT__
foo 1
bar 23
baz 4
foo 53
foobar 6
whatever 99
...
__EOT__
(Ye olde shell doesn't support #!notation, shell functions, and doesn't
support $( command substitution syntax.)
And the various test runs (fails miserably under bash shell - I won't
bother showing that - it apparently can't properly parse case statement
with pattern(s) in that context) ...
$ cat /etc/debian_version && ls -l /bin/sh
6.0.2
lrwxrwxrwx 1 root root 4 Apr 13 12:16 /bin/sh -> dash
$ ./eval
count for foo is 54
count for bar is 23
count for baz is 4
count for foobar is 6
$ ./eval.old
count for foo is 54
count for bar is 23
count for baz is 4
count for foobar is 6
$
And under ye olde Bourne shell from Unix 7th edition circa 1979:
$ ./eval.old
count for foo is 54
count for bar is 23
count for baz is 4
count for foobar is 6
$
I'll leave analyzing/explaining/commenting the code as an exercise.
Standard caveats/disclaimers apply.
references/excerpts:
sh(1)
expr(1)
cat(1)
http://www.rawbw.com/~mp/unix/sh/
http://www.rawbw.com/~mp/unix/sh/#Good_Programming_Practices
http://www.weak.org/pipermail/buug/2011-August/003884.html et. seq.
More information about the buug
mailing list