[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: python, then C++, or C++ from the start?



* From: martin f krafft <madduck@debian.org>
* Date: Thu, 31 May 2007 10:37:54 +0200
* Organization: Debian GNU/Linux

> also sprach Oleg Verych <olecom@flower.upol.cz> [2007.05.31.1013 +0200]:
>> Then call me !
>
> Do you have a POSIX-compatible solution to the problem of setting
> variables inside while loops?
>
> See http://blog.madduck.net/geek/2006.05.29-shell-sucks

,---[ blog entry, Mon, 29 May 2006 ]---
|
|Shell sucks
|
|   Shell programming must be the most depressive waste of time out there,
|   and if only because things sometimes just don't work the way you'd
|   expect from a scripting language.

Yes... And main reason is, IMHO, shell is a kernel syscalls (open, read,
write, pipe, fork, etc.) manipulation tool with some helper
functionality, it's not a programming thing.

|   Take the following simple examples in bash (3):
|for i in 1 2 3; do
|  echo $i
|  exit 1
|done
| echo END
|
|==> 1
|
|echo -e "1\n2\n3" | while read i; do
|  echo $i
|   exit 1
|done
|echo END
|
|==> 1
|==> END
| 
|   WTF? The result of the first -- the whole script exits entirely after
|   the first iteration -- is what I expect. After all, I did not call
|   break, but exit. The result of the second is totally unexpected. I
|   understand that for and while each execute their iterations in a
|   subshell, but why don't these behave in the same way?

I'd say, the *fine* reason can be found after reading pipe(7), pipe(2).
Pipe is so frequently mentioned and used in the UNIX environment by
shells, but programmers with their applications use it fairly seldom. 

(in short: syscall pipe(2) can be used only by two processes, i.e fork)

However as noted in some historical notes ever, it was a time, where
all that wasn't that good. Shell implementations ran loops with
redirected I/O (<,>) in subshells also, while this not require
forking[0]. Now it's OK.

[0] UNIX Power Tools <http://www.unix.org.ua/orelly/unix/upt/ch45_23.htm>

|   The fix here is a || exit $? following the done keyword. Urks!

Obviously, it's an another process. Syntax is wired, but hey, it's the
UNIX power!

|
|   Here's another one: the task is simply to output the number of 1's in
|   the output (yeah, I realise grep -c can do this too):
|cnt=0
|echo -e "2\n1\n3\n1" | while read i; do
|    case $i in
|      1) cnt=$((cnt + 1));;
|    esac
|  done
|echo $cnt
|
| ==> 0
|
|   The reason? The while subshell receives a copy of the caller
|   environment, so when it updates $cnt, it only updates a copy of the
|   actual variable, leaving the original untouched. Similarly, a variable
|   defined inside the subshell won't be available after the while loop is
|   done.

Aha, you knew that! But it's a kind of confusion. Again, *subshell* is
another process, another process is not necessarily a shell. See also
38.4 in the [0]. Thus same result must be with `` and $().

Funny thing to note is, that this script already bash specific, because
of `echo -e'. Forget it, please, use `printf %b "..."' instead.

|
|   I cannot find a fix that's not bash specific.
|
|   Of course, it works fine when I replace the while loop by a for loop:
|cnt=0
| for i in 2 1 3 1; do
|    case $i in
|      1) cnt=$((cnt + 1));;
|    esac
|  done
|echo $cnt
|
|==> 2
|
|   Great, isn't it?
|
|   And I am not surprised that zsh gets it right. Why would anyone
|   actually want to use bash?

Maybe zsh is a shell for programmers ;)

==[self, about programmers] I developed one simple way of being useful
for any kind of Linux development, while i had wonderful academia time.

Become a User (not user or luser) of your system first, before you start
to develop and code anything. Unfortunately big and expensive UNIX setups
were not so widely available, and many of use came back there only after
M$ brainwashing, dot com bubbles, HTML, Javascript XML and such
$(</dev/random dd bs=13 count=1) [note subshell! ;] ==[ish]

|
|   Update: here's Joey's explanation for the behaviour, and Clint's
|   account for why zsh does it right. I still can't figure out how to
|   count the 1's.

Joey is talking about lot of shell programming and POSIX, while using
`echo -e` :D

|
|   Update ^2: Axel Liljencrantz sent in this message.
|
||grep ...|read foo bar baz
||
||Which is very often useful. As you say in your blog, zsh fares quite a
||bit better since the last process in a pipeline is not run in a
||subshell, which at least takes care of the most common problems. But
||the fundamental idea of subshells is, in my opinion, broken. You
||simply can't do variable assignment or perform many other builtins
||with any reliability when using pipelines or command substitutions.
||
||I've tried to write a shell (called fish) that never ever forks of a
||subshell...

It's possible to do variable assigment `eval', `. file', [0].
[But programmer chooses to code more...]

| 
|   Update ^3: Alexander Sieck showed me that process substitution
|   ("<(command)") instead of the pipe does work (for both cases):
|while read i; do
|  echo $i
|  exit 1
|done < <(echo -e "1\n2\n3")
|echo END
|
|   Of course that's nowhere near POSIX compliance...

Don't know how and where it works.

|
|   Update ^4: my solution, which allows for spaces in the lines:
| IFSOLD=${IFS:-}
|IFS='
|' # yes, a newline
|for i in $(the_command): do
                        ^;
|  IFS=$IFSOLD
|  ...
|done

Where, how and why word splitting works, is another miss-understood
feature. I thing man sh (with /bin/sh=dash) and some practice will
solve this problem. If data size may be unpredictably big, processing
of available lines must be done immediately using pipes is much
better choice. How to recover assignments we already know, don't we.

Only one thing: instead of wired new lines (or even tabs!), just use
something like:

-*- shell -*-
$dash
flower:-$ a="`printf '\n_'`"
flower:-$ a="${a%_}"
flower:-$ set | grep -A2 -e '^a='
a='
'
flower:-$ a=`printf '\t'`
flower:-$ set | grep -A2 -e '^a='
a='     '
flower:-$
-*-

First trick is used only if `\n' is on the end.

|
|
`---[ end ]

Hope that helps, programmers !

(Sorry, for delay, i waited `g' response by e-mail, like first time :)

Good bye.
--
-o--=O`C  info emacs : faq        /. .\ ( is there any reason to live? )
 #oo'L O  info make  : not found      o (    yes --- R.I.P. FSF+RMS    )
<___=E M  man gcc    : not found    `-- ( viva Debian Operating System )



Reply to: