Allow shell scripts to run with non-Bash /bin/sh
PATCH 1/1 Allow shell scripts to run with non-Bash /bin/sh
Good day.
I had found that FreeBSD's /bin/sh refuses to work with git 1.5.3.2.
correctly: no flags are recognized. The details and fix are below. I don't currently know if the Bash's behaviour is POSIXly correct
or the 'case' statement semantics is not very well defined. But
the following patch fixes the things for the FreeBSD.
Here we go.
-----
Option parsing in the Git shell scripts uses the construct 'while case "$#" in 0) break ;; esac; do ... done'. This is neat, because it needs no external commands invocation. But in the case when /bin/sh is not GNU Bash (for example, on FreeBSD) this cycle will not be executed at all.
The fix is to add the case branch '*) : ;;'. It also needs no external commands invocation and it does its work, because ':' always returns zero.
--- git-am.sh | 2 +- git-clean.sh | 2 +- git-commit.sh | 2 +- git-fetch.sh | 2 +- git-filter-branch.sh | 2 +- git-instaweb.sh | 2 +- git-ls-remote.sh | 2 +- git-merge.sh | 2 +- git-mergetool.sh | 2 +- git-pull.sh | 2 +- git-quiltimport.sh | 2 +- git-rebase--interactive.sh | 2 +- git-rebase.sh | 2 +- git-repack.sh | 2 +- git-reset.sh | 2 +- git-submodule.sh | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-)
git-am.sh +1 -1
@@ -109,7 +109,7 @@ dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= resolvemsg= resume= git_apply_opt= -while case "$#" in 0) break;; esac +while case "$#" in 0) break;; *) : ;; esac do case "$1" in -d=*|--d=*|--do=*|--dot=*|--dote=*|--dotes=*|--dotest=*)
git-clean.sh +1 -1
@@ -26,7 +26,7 @@ rmrf="rm -rf --" rm_refuse="echo Not removing" echo1="echo" -while case "$#" in 0) break ;; esac +while case "$#" in 0) break ;; *) : ;; esac do case "$1" in -d)
git-commit.sh +1 -1
@@ -89,7 +89,7 @@ force_author= only_include_assumed= untracked_files= templatefile="`git config commit.template`" -while case "$#" in 0) break;; esac +while case "$#" in 0) break;; *) : ;; esac do case "$1" in -F|--F|-f|--f|--fi|--fil|--file)
git-fetch.sh +1 -1
@@ -27,7 +27,7 @@ shallow_depth= no_progress= test -t 1 || no_progress=--no-progress quiet= -while case "$#" in 0) break ;; esac +while case "$#" in 0) break ;; *) : ;; esac do case "$1" in -a|--a|--ap|--app|--appe|--appen|--append)
git-filter-branch.sh +1 -1
@@ -105,7 +105,7 @@ filter_tag_name= filter_subdir= orig_namespace=refs/original/ force= -while case "$#" in 0) usage;; esac +while case "$#" in 0) usage;; *) : ;; esac do case "$1" in --)
git-instaweb.sh +1 -1
@@ -61,7 +61,7 @@ stop_httpd () { test -f "$fqgitdir/pid" && kill `cat "$fqgitdir/pid"` } -while case "$#" in 0) break ;; esac +while case "$#" in 0) break ;; *) : ;; esac do case "$1" in --stop|stop)
git-ls-remote.sh +1 -1
@@ -13,7 +13,7 @@ die () { } exec= -while case "$#" in 0) break;; esac +while case "$#" in 0) break;; *) : ;; esac do case "$1" in -h|--h|--he|--hea|--head|--heads)
git-merge.sh +1 -1
@@ -122,7 +122,7 @@ merge_name () { case "$#" in 0) usage ;; esac have_message= -while case "$#" in 0) break ;; esac +while case "$#" in 0) break ;; *) : ;; esac do case "$1" in -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
git-mergetool.sh +1 -1
@@ -268,7 +268,7 @@ merge_file () { cleanup_temp_files } -while case $# in 0) break ;; esac +while case $# in 0) break ;; *) : ;; esac do case "$1" in -t|--tool*)
git-pull.sh +1 -1
@@ -16,7 +16,7 @@ test -z "$(git ls-files -u)" || die "You are in the middle of a conflicted merge." strategy_args= no_summary= no_commit= squash= -while case "$#,$1" in 0) break ;; *,-*) ;; *) break ;; esac +while case "$#,$1" in 0) break ;; *,-*) : ;; *) break ;; esac do case "$1" in -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
git-quiltimport.sh +1 -1
@@ -5,7 +5,7 @@ SUBDIRECTORY_ON=Yes dry_run="" quilt_author="" -while case "$#" in 0) break;; esac +while case "$#" in 0) break;; *) : ;; esac do case "$1" in --au=*|--aut=*|--auth=*|--autho=*|--author=*)
git-rebase--interactive.sh +1 -1
@@ -317,7 +317,7 @@ do_rest () { done } -while case $# in 0) break ;; esac +while case $# in 0) break ;; *) : ;; esac do case "$1" in --continue)
git-rebase.sh +1 -1
@@ -122,7 +122,7 @@ finish_rb_merge () { is_interactive () { test -f "$dotest"/interactive || - while case $#,"$1" in 0,|*,-i|*,--interactive) break ;; esac + while case $#,"$1" in 0,|*,-i|*,--interactive) break ;; *) : ;; esac do shift done && test -n "$1"
git-repack.sh +1 -1
@@ -9,7 +9,7 @@ SUBDIRECTORY_OK='Yes' no_update_info= all_into_one= remove_redundant= local= quiet= no_reuse= extra= -while case "$#" in 0) break ;; esac +while case "$#" in 0) break ;; *) : ;; esac do case "$1" in -n) no_update_info=t ;;
git-reset.sh +1 -1
@@ -11,7 +11,7 @@ require_work_tree update= reset_type=--mixed unset rev -while case $# in 0) break ;; esac +while case $# in 0) break ;; *) : ;; esac do case "$1" in --mixed | --soft | --hard)
git-submodule.sh +1 -1
@@ -251,7 +251,7 @@ modules_list() done } -while case "$#" in 0) break ;; esac +while case "$#" in 0) break ;; *) : ;; esac do case "$1" in add)
Signature · 3 lines
1.5.3.2 -- Eygene
Quoted 10 lines · Eygene Ryabinkin
Good day.
I had found that FreeBSD's
/bin/shrefuses to work with git 1.5.3.2.
correctly: no flags are recognized. The details and fix are below. I don't currently know if the Bash's behaviour is POSIXly correct
or the 'case' statement semantics is not very well defined. But
the following patch fixes the things for the FreeBSD.Here we go.
Quoted 7 lines
-----
Option parsing in the Git shell scripts uses the construct 'while case "$#" in 0) break ;; esac; do ... done'. This is neat, because it needs no external commands invocation. But in the case when
/bin/shis not GNU Bash (for example, on FreeBSD) this cycle will not be executed at all.
I do not doubt that "while case $# in 0) break ;; esac" does not work for your shell. But I think the above comment is grossly misleading.
Don't mention bash there. You sound as if you are blaming bashism, but the thing is, your shell is simply broken.
You have other choices than bash on BSD don't you?
My quick test shows that ksh, pdksh and dash seem to work correctly. This idiom is what I picked up around late 80's from somebody, and kept using on many variants of Unices. I would find quite surprising that something that claims to be a shell does not work correctly. Even /bin/sh that comes with Solaris seems to work correctly, which should tell you something.
OpenBSD's /bin/sh seems to be Ok; I do not know whose shell they use, but it seems to be hard-linked to /bin/ksh which is pdksh.
Quoted 3 lines · Junio C Hamano
I do not doubt that "while case $# in 0) break ;; esac" does not work for your shell. But I think the above comment is grossly misleading.
Personally, I find this idiom distasteful.
I'd do either
while case $# in 0) false ;; *) true esac
or, more likely
while : do case $# in 0) break;; esac
But doing a break inside of the while _condition_ rather than the body just feels wrong to me.
Signature · 1 line
David Kastrup
Quoted 2 lines · David Kastrup
But doing a break inside of the while _condition_ rather than the body just feels wrong to me.
Sorry, but that is not the issue on the thread is about. BSD shell is failing the whole case statement when there is no matching case arm.
Quoted 8 lines · Junio C Hamano
Quoted 2 lines · David Kastrup
But doing a break inside of the while _condition_ rather than the body just feels wrong to me.
Sorry, but that is not the issue on the thread is about. BSD shell is failing the whole case statement when there is no matching case arm.
I did not mean "BSD shell" in general here. The shell Eygene uses on his (unspecified version of) FreeBSD box is failing.
Quoted 6 lines · Junio C Hamano
Quoted 2 lines · David Kastrup
But doing a break inside of the while _condition_ rather than the body just feels wrong to me.
Sorry, but that is not the issue on the thread is about.
It is still relevant:
Quoted 2 lines
BSD shell is failing the whole case statement when there is no matching case arm.
Sure, that is a bug. But in my opinion the idiom as such is ugly enough not be worth keeping anyhow. The proposal of the patch submitter was making an already ugly idiom even uglier for the sake of his shell. I agree that this is not the way to go.
I was proposing replacing the idiom by something which I find cleaner. That it will most likely work with the original poster's shell is more or less a side effect. At least it is a side effect that might motivate somebody else rather than me to do the cleanup work (and have a buggy test shell to see whether he got everything indeed).
Signature · 1 line
David Kastrup
git-am.sh dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= · line 113
Quoted 12 lines · Eygene Ryabinkin
Good day.
I had found that FreeBSD's
/bin/shrefuses to work with git 1.5.3.2.
correctly: no flags are recognized.git-am.sh +1
-1@@ -109,7 +109,7 @@ dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= resolvemsg= resume= git_apply_opt= -while case "$#" in 0) break;; esac +while case "$#" in 0) break;; *) : ;; esac do
I am assuming that this works around _a_ bug in that /bin/sh; I would make sure I understand the nature of the bug. Is it Ok to understand that with that shell, after this construct runs:
case <some word> in
<case arm #1>)
something ;;
<case arm #2>)
something else ;;
esacthe status from the whole case statement is false, when <some word> does not match any of the glob patterns listed in any of the case arm?
That is, what does the shell say if you do this?
case Ultra in
Super)
false ;;
Hyper)
true ;;
esac &&
echo case returned okThe reason I ask is because
while case $# in 0) ... esac
do
...
doneis not the only place the status from "case" itself matters in our scripts. There are places that do
something &&
case ... in
...
esac &&
something elseand we would need to add no-op match-everything arm to all of such case statements in our scripts.
Besides test scripts, there is one in git-ls-remote.sh which you seem to have missed.
Junio, good day.
Quoted 6 lines · Junio C Hamano
Quoted 5 lines
Option parsing in the Git shell scripts uses the construct 'while case "$#" in 0) break ;; esac; do ... done'. This is neat, because it needs no external commands invocation. But in the case when
/bin/shis not GNU Bash (for example, on FreeBSD) this cycle will not be executed at all.
Quoted 6 lines
I do not doubt that "while case $# in 0) break ;; esac" does not work for your shell. But I think the above comment is grossly misleading.
Don't mention bash there. You sound as if you are blaming bashism, but the thing is, your shell is simply broken.
OK, you're right. Especially if /bin/sh from Solaris and OpenBSD are working and they are not Bash. But I would not tell that the shell is broken now -- I had not seen the POSIX specification. Does it specifies how the shell should work in this case?
Quoted 1 line
You have other choices than bash on BSD don't you?
Did not understand the question, sorry. The thing is that FreeBSD has /bin/sh that is derived from the original Berkeley shell. And it is desirable to have it working with Git script, since I don't want to make bash (or whatever shell that is not /bin/sh) a dependency for the port.
Quoted 9 lines
My quick test shows that ksh, pdksh and dash seem to work correctly. This idiom is what I picked up around late 80's from somebody, and kept using on many variants of Unices. I would find quite surprising that something that claims to be a shell does not work correctly. Even
/bin/shthat comes with Solaris seems to work correctly, which should tell you something.OpenBSD's
/bin/shseems to be Ok; I do not know whose shell they use, but it seems to be hard-linked to/bin/kshwhich is pdksh.
OK, I think I need to find out why FreeBSD's /bin/sh behaves like this, because the test you propose on your next message works. See below.
By the way, my FreeBSD is 7-CURRENT, but I'll test on 6-STABLE and perhaps on 4-STABLE on Monday.
Quoted 16 lines · Junio C Hamano
I am assuming that this works around _a_ bug in that
/bin/sh; I would make sure I understand the nature of the bug. Is it Ok to understand that with that shell, after this construct runs:case <some word> in <case arm #1>) something ;; <case arm #2>) something else ;; esacthe status from the whole case statement is false, when <some word> does not match any of the glob patterns listed in any of the case arm?
That is, what does the shell say if you do this?
Quoted 7 lines
case Ultra in
Super)
false ;;
Hyper)
true ;;
esac &&
echo case returned okIt says 'case returned ok', so I will try to understand why it works here and does not work in the 'while' construct.
Thanks for the pointer!
Signature · 1 line
Eygene
Quoted 13 lines · Eygene Ryabinkin
By the way, my FreeBSD is 7-CURRENT, but I'll test on 6-STABLE and perhaps on 4-STABLE on Monday.
...Quoted 9 lines
That is, what does the shell say if you do this?
case Ultra in Super) false ;; Hyper) true ;; esac && echo case returned ok
Quoted 2 lines
It says 'case returned ok', so I will try to understand why it works here and does not work in the 'while' construct.
I vaguely recall somebody else had exactly this issue and he concluded that the shell was busted. I do not recall the details of the story but interestingly, if he did something that accesses "$#" before the problematic "while case $# in ..." the shell behaved for him in his experiments.
Just to make sure you do not misunderstand me, I am not trying to be difficult. I am trying to assess (1) if it is sensible to support that broken shell, and (2) if so what the exact breakage is, especially because as the above shows the breakage does not look like what your "fix" literally suggests, and what is involved in working it around.
Also by my comment about "/bin/sh and bash not being the only shells available on FreeBSD", I did not mean that you should change your /bin/sh. You can build git with SHELL_PATH make varilable pointing at a non-broken shell, which does not have to be installed as /bin/sh.
Quoted 5 lines · Junio C Hamano
I vaguely recall somebody else had exactly this issue and he concluded that the shell was busted. I do not recall the details of the story but interestingly, if he did something that accesses "$#" before the problematic "while case $# in ..." the shell behaved for him in his experiments.
That is what I did notice, just accessing $# fixed later uses of it.
Quoted 5 lines
Also by my comment about "
/bin/shand bash not being the only shells available on FreeBSD", I did not mean that you should change your/bin/sh. You can build git withSHELL_PATHmake varilable pointing at a non-broken shell, which does not have to be installed as/bin/sh.
If one's installing from the ports tree, then the port should depend on a non-broken shell and set SHELL_PATH. And as for installing by hand, just print out a warning that SHELL_PATH points to a broken shell and be done with it. This is a FreeBSD bug, not a git one.
I had been meaning to write up a bug about this using a small test case, but I couldn't reproduce it.
Adam
Quoted 4 lines · Eygene Ryabinkin
OK, you're right. Especially if
/bin/shfrom Solaris and OpenBSD are working and they are not Bash. But I would not tell that the shell is broken now -- I had not seen the POSIX specification. Does it specifies how the shell should work in this case?
I have always been assuming it to be the case (this construct is not my invention but is an old school idiom I just inherited from my mentor) and never looked at the spec recently, but I re-read it just to make sure. The answer is yes.
Visit http://www.opengroup.org/onlinepubs/000095399/ and follow "Shell and Utilities volume (XCU)" and then "Case conditional construct".
Exit Status
The exit status of case shall be zero if no patterns are matched. Otherwise, the exit status shall be the exit status of the last command executed in the compound-list.
So, as David suggests, if
false
case Ultra in
Super) false ;;
Hyper) true ;;
esac && echo case returned okdoes not say "case returned ok", then the shell has a bit of problem.
Junio, *, good day.
Quoted 5 lines · Junio C Hamano
Quoted 4 lines
OK, you're right. Especially if
/bin/shfrom Solaris and OpenBSD are working and they are not Bash. But I would not tell that the shell is broken now -- I had not seen the POSIX specification. Does it specifies how the shell should work in this case?
Quoted 8 lines
I have always been assuming it to be the case (this construct is not my invention but is an old school idiom I just inherited from my mentor) and never looked at the spec recently, but I re-read it just to make sure. The answer is yes.
Visit http://www.opengroup.org/onlinepubs/000095399/ and follow "Shell and Utilities volume (XCU)" and then "Case conditional construct".
Yes, thanks for the pointer.
Quoted 10 lines
So, as David suggests, if
false case Ultra in Super) false ;; Hyper) true ;; esac && echo case returned okdoes not say "case returned ok", then the shell has a bit of problem.
Correct: the current /bin/sh for FreeBSD does not set zero exit code if no case patterns were matched. So, I apologize for my quick decision on the non-brokenness of the /bin/sh -- it is broken.
I had fixed the shell and filed the problem report. May be the change will be incorporated into the future release of FreeBSD. Meanwhile, I had added workarounds to the other places Junio mentioned in his follow-up and will try to push this patch to the FreeBSD port of Git. The explanation had been changed too ;))
Thanks to all people who helped me to realize what is wrong and where!
Signature · 1 line
Eygene
Quoted 11 lines · Junio C Hamano
Quoted 4 lines · Eygene Ryabinkin
OK, you're right. Especially if
/bin/shfrom Solaris and OpenBSD are working and they are not Bash. But I would not tell that the shell is broken now -- I had not seen the POSIX specification. Does it specifies how the shell should work in this case?I have always been assuming it to be the case (this construct is not my invention but is an old school idiom I just inherited from my mentor) and never looked at the spec recently, but I re-read it just to make sure. The answer is yes.
Independent of that: would you mind a patch replacing that idiom with
while : do case xxx) break; esac
instead? I find breaking out of the condition rather than the body awkward, and I find a non-matching case statement, POSIX or not, quite unobvious in the place of a true while condition.
It is a bit too much of cleverness for my taste. Never mind that the current FreeBSD shell does not understand it due to being buggy: I find that this is not very readable to the human reader either without a double take.
Signature · 1 line
David Kastrup, Kriemhildstr. 15, 44793 Bochum
Quoted 6 lines · David Kastrup
Independent of that: would you mind a patch replacing that idiom with
while : do case xxx) break; esac
instead? I find breaking out of the condition rather than the body awkward,...
I do not have any problem with your approach at all.
While I personally do not think it improves readability much, I do not think it hurts either. And it is a valid workaround for FBSD issue, so why not.
But on one condition, however. If it is done correctly with double semi-colons before "esac" ;-)
Thanks.
Quoted 10 lines · Junio C Hamano
Quoted 6 lines · David Kastrup
Independent of that: would you mind a patch replacing that idiom with
while : do case xxx) break; esac
instead? I find breaking out of the condition rather than the body awkward,...
I do not have any problem with your approach at all.
Too bad sh does... I need to write
while :; do case xxx) break; esac
instead which is slightly uglier concerning visual aesthetics.
Quoted 2 lines
But on one condition, however. If it is done correctly with double semi-colons before "esac" ;-)
My feeling about this is that double semi-colons before "esac" are redundant and similarly ugly as ";" before "end" in Pascal. However, looking at the existing git scripts it would seem that the redundant style is ubiquitous anyway, so it would be inconsistent to do this differently just within the idiom.
Signature · 1 line
David Kastrup, Kriemhildstr. 15, 44793 Bochum
PATCH 1/1 Supplant the "while case ... break ;; esac" idiom
A lot of shell scripts contained stuff starting with
while case "$#" in 0) break ;; esac
and similar. I consider breaking out of the condition instead of the body od the loop ugly, and the implied "true" value of the non-matching case is not really obvious to humans at first glance. It happens not to be obvious to some BSD shells, either, but that's because they are not POSIX-compliant. In most cases, this has been replaced by a straight condition using "test". "case" has the advantage of being faster than "test" on vintage shells where "test" is not a builtin. Since none of them is likely to run the git scripts, anyway, the added readability should be worth the change.
A few loops have had their termination condition expressed differently.
---
Ok, this is not really what we have been talking about except in one case, but I think it is actually more of an improvement.
contrib/examples/git-gc.sh | 2 +- contrib/examples/git-reset.sh | 2 +- contrib/examples/git-tag.sh | 2 +- contrib/examples/git-verify-tag.sh | 2 +- git-am.sh | 2 +- git-clean.sh | 2 +- git-commit.sh | 2 +- git-fetch.sh | 2 +- git-filter-branch.sh | 3 ++- git-instaweb.sh | 2 +- git-ls-remote.sh | 2 +- git-merge.sh | 2 +- git-mergetool.sh | 2 +- git-pull.sh | 6 +++--- git-quiltimport.sh | 2 +- git-rebase--interactive.sh | 2 +- git-rebase.sh | 5 ++--- git-repack.sh | 2 +- git-submodule.sh | 2 +- 19 files changed, 23 insertions(+), 23 deletions(-)
contrib/examples/git-gc.sh +1 -1
@@ -9,7 +9,7 @@ SUBDIRECTORY_OK=Yes . git-sh-setup no_prune=: -while case $# in 0) break ;; esac +while test $# != 0 do case "$1" in --prune)
contrib/examples/git-reset.sh +1 -1
@@ -11,7 +11,7 @@ require_work_tree update= reset_type=--mixed unset rev -while case $# in 0) break ;; esac +while test $# != 0 do case "$1" in --mixed | --soft | --hard)
contrib/examples/git-tag.sh +1 -1
@@ -14,7 +14,7 @@ username= list= verify= LINES=0 -while case "$#" in 0) break ;; esac +while test "$#" != 0 do case "$1" in -a)
contrib/examples/git-verify-tag.sh +1 -1
@@ -5,7 +5,7 @@ SUBDIRECTORY_OK='Yes' . git-sh-setup verbose= -while case $# in 0) break;; esac +while test $# != 0 do case "$1" in -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
git-am.sh +1 -1
@@ -109,7 +109,7 @@ dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= resolvemsg= resume= git_apply_opt= -while case "$#" in 0) break;; esac +while test "$#" != 0 do case "$1" in -d=*|--d=*|--do=*|--dot=*|--dote=*|--dotes=*|--dotest=*)
git-clean.sh +1 -1
@@ -26,7 +26,7 @@ rmrf="rm -rf --" rm_refuse="echo Not removing" echo1="echo" -while case "$#" in 0) break ;; esac +while test "$#" != 0 do case "$1" in -d)
git-commit.sh +1 -1
@@ -89,7 +89,7 @@ force_author= only_include_assumed= untracked_files= templatefile="`git config commit.template`" -while case "$#" in 0) break;; esac +while test "$#" != 0 do case "$1" in -F|--F|-f|--f|--fi|--fil|--file)
git-fetch.sh +1 -1
@@ -27,7 +27,7 @@ shallow_depth= no_progress= test -t 1 || no_progress=--no-progress quiet= -while case "$#" in 0) break ;; esac +while test "$#" != 0 do case "$1" in -a|--a|--ap|--app|--appe|--appen|--append)
git-filter-branch.sh +2 -1
@@ -105,8 +105,9 @@ filter_tag_name= filter_subdir= orig_namespace=refs/original/ force= -while case "$#" in 0) usage;; esac +while : do + test "$#" = 0 && usage case "$1" in --) shift
git-instaweb.sh +1 -1
@@ -61,7 +61,7 @@ stop_httpd () { test -f "$fqgitdir/pid" && kill `cat "$fqgitdir/pid"` } -while case "$#" in 0) break ;; esac +while test "$#" != 0 do case "$1" in --stop|stop)
git-ls-remote.sh +1 -1
@@ -13,7 +13,7 @@ die () { } exec= -while case "$#" in 0) break;; esac +while test "$#" != 0 do case "$1" in -h|--h|--he|--hea|--head|--heads)
git-merge.sh +1 -1
@@ -122,7 +122,7 @@ merge_name () { case "$#" in 0) usage ;; esac have_message= -while case "$#" in 0) break ;; esac +while test "$#" != 0 do case "$1" in -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
git-mergetool.sh +1 -1
@@ -268,7 +268,7 @@ merge_file () { cleanup_temp_files } -while case $# in 0) break ;; esac +while test $# != 0 do case "$1" in -t|--tool*)
git-pull.sh +3 -3
@@ -16,7 +16,7 @@ test -z "$(git ls-files -u)" || die "You are in the middle of a conflicted merge." strategy_args= no_summary= no_commit= squash= -while case "$#,$1" in 0) break ;; *,-*) ;; *) break ;; esac +while : do case "$1" in -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
@@ -46,8 +46,8 @@ do -h|--h|--he|--hel|--help) usage ;; - -*) - # Pass thru anything that is meant for fetch. + *) + # Pass thru anything that may be meant for fetch. break ;; esac
git-quiltimport.sh +1 -1
@@ -5,7 +5,7 @@ SUBDIRECTORY_ON=Yes dry_run="" quilt_author="" -while case "$#" in 0) break;; esac +while test "$#" != 0 do case "$1" in --au=*|--aut=*|--auth=*|--autho=*|--author=*)
git-rebase--interactive.sh +1 -1
@@ -317,7 +317,7 @@ do_rest () { done } -while case $# in 0) break ;; esac +while test $# != 0 do case "$1" in --continue)
git-rebase.sh +2 -3
@@ -122,15 +122,14 @@ finish_rb_merge () { is_interactive () { test -f "$dotest"/interactive || - while case $#,"$1" in 0,|*,-i|*,--interactive) break ;; esac - do + while :; do case $#,"$1" in 0,|*,-i|*,--interactive) break ;; esac shift done && test -n "$1" } is_interactive "$@" && exec git-rebase--interactive "$@" -while case "$#" in 0) break ;; esac +while test "$#" != 0 do case "$1" in --continue)
git-repack.sh +1 -1
@@ -9,7 +9,7 @@ SUBDIRECTORY_OK='Yes' no_update_info= all_into_one= remove_redundant= local= quiet= no_reuse= extra= -while case "$#" in 0) break ;; esac +while test "$#" != 0 do case "$1" in -n) no_update_info=t ;;
git-submodule.sh +1 -1
@@ -251,7 +251,7 @@ modules_list() done } -while case "$#" in 0) break ;; esac +while test "$#" != 0 do case "$1" in add)
Signature · 1 line
1.5.3.1.96.g4568
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Review
Quoted 2 lines · David Kastrup
(quoted diff) +1
-1-while case $# in 0) break ;; esac +while test $# != 0
Wouldn't -ne be better ?
Mike
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Quoted 5 lines · Mike Hommey
Review
Quoted 2 lines · David Kastrup
(quoted diff) +1
-1-while case $# in 0) break ;; esac +while test $# != 0Wouldn't -ne be better ?
Why?
Signature · 1 line
David Kastrup, Kriemhildstr. 15, 44793 Bochum
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Quoted 9 lines · David Kastrup
Quoted 5 lines · Mike Hommey
Review
Quoted 2 lines · David Kastrup
(quoted diff) +1
-1-while case $# in 0) break ;; esac +while test $# != 0Wouldn't -ne be better ?
Why?
Because -ne does a numeric comparison, != does a string comparison, and it's a numeric comparison happening, semantically speaking.
Dave.
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Quoted 9 lines · David Symonds
Quoted 8 lines · David Kastrup
Quoted 5 lines · Mike Hommey
Review
Quoted 2 lines · David Kastrup
(quoted diff) +1
-1-while case $# in 0) break ;; esac +while test $# != 0Wouldn't -ne be better ?
Quoted 4 lines
Quoted 1 line
Why?
Because -ne does a numeric comparison, != does a string comparison, and it's a numeric comparison happening, semantically speaking.
I don't see the point in converting $# and 0 into numbers before comparing them. "!=" is quite more readable, and the old code also compared the strings.
Signature · 1 line
David Kastrup
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Quoted 11 lines · David Kastrup
Quoted 9 lines · David Symonds
Quoted 8 lines · David Kastrup
Quoted 5 lines · Mike Hommey
Review
Quoted 2 lines · David Kastrup
(quoted diff) +1
-1-while case $# in 0) break ;; esac +while test $# != 0Wouldn't -ne be better ?
Quoted 8 lines
Quoted 4 lines
Quoted 1 line
Why?
Because -ne does a numeric comparison, != does a string comparison, and it's a numeric comparison happening, semantically speaking.
I don't see the point in converting $# and 0 into numbers before comparing them. "!=" is quite more readable, and the old code also compared the strings.
Fwiw $# already is a number. Hence test $# -ne 0 is definitely a
better test.
$# != 0 would yield sth like (strcmp(sprintf("%d", argc), "0")) $# -ne 0 would yield sth like (argc != atoi("0")).
Not that it matters much, but the latter looks better to me.
Signature · 3 lines
·O· Pierre Habouzit ··O [email protected] OOO http://www.madism.org
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Quoted 21 lines · Pierre Habouzit
Quoted 11 lines · David Kastrup
Quoted 9 lines · David Symonds
Quoted 8 lines · David Kastrup
Quoted 5 lines · Mike Hommey
Review
Quoted 2 lines · David Kastrup
(quoted diff) +1
-1-while case $# in 0) break ;; esac +while test $# != 0Wouldn't -ne be better ?
Quoted 8 lines
Quoted 4 lines
Quoted 1 line
Why?
Because -ne does a numeric comparison, != does a string comparison, and it's a numeric comparison happening, semantically speaking.
I don't see the point in converting $# and 0 into numbers before comparing them. "!=" is quite more readable, and the old code also compared the strings.
Quoted 5 lines
Fwiw $# already is a number. Hence test $# -ne 0 is definitely a
better test.$# != 0 would yield sth like (
strcmp(sprintf("%d", argc), "0")) $# -ne 0 would yield sth like (argc !=atoi("0")).
Of course this holds only for shell where test/[ is a builtin, which
is the at least the case for zsh, bash, and dash (but not posh).
Signature · 3 lines
·O· Pierre Habouzit ··O [email protected] OOO http://www.madism.org
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Hi,
Quoted 22 lines · Pierre Habouzit
Quoted 21 lines · Pierre Habouzit
Quoted 19 lines · David Kastrup
Quoted 9 lines · David Symonds
Quoted 8 lines · David Kastrup
Quoted 5 lines · Mike Hommey
Review
Quoted 2 lines · David Kastrup
(quoted diff) +1
-1-while case $# in 0) break ;; esac +while test $# != 0Wouldn't -ne be better ?
Quoted 4 lines
Quoted 1 line
Why?
Because -ne does a numeric comparison, != does a string comparison, and it's a numeric comparison happening, semantically speaking.
I don't see the point in converting $# and 0 into numbers before comparing them. "!=" is quite more readable, and the old code also compared the strings.
Quoted 8 lines
Quoted 5 lines
Fwiw $# already is a number. Hence test $# -ne 0 is definitely a
better test.$# != 0 would yield sth like (
strcmp(sprintf("%d", argc), "0")) $# -ne 0 would yield sth like (argc !=atoi("0")).Of course this holds only for shell where test/[ is a builtin, which
is the at least the case for zsh, bash, and dash (but not posh).
The reason we used "case" is that this has always been a builtin (has to be, because it changes workflow).
Therefore I am somewhat uneasy that the patch went in so easily, especially given a message that flies in the face of our endeavours to make git less dependent on any given shell (as long as it is not broken to begin with).
Ciao,
Dscho
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Quoted 4 lines · Johannes Schindelin
The reason we used "case" is that this has always been a builtin (has to be, because it changes workflow).
Therefore I am somewhat uneasy that the patch went in so easily,
It didn't yet.
Quoted 3 lines
especially given a message that flies in the face of our endeavours to make git less dependent on any given shell (as long as it is not broken to begin with).
"test" is not actually a shell dependency since it is available as an external when not available as builtin. And if you really want to prefer "case" over "test" because the latter is not a built-in in a small number of shells, then it should be done consistently everywhere and not just in code I touch.
Signature · 1 line
David Kastrup
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
In reply to Johannes SchindelinQuoted 6 lines · Johannes Schindelin
Quoted 5 lines
Quoted 2 lines
$# != 0 would yield sth like (
strcmp(sprintf("%d", argc), "0")) $# -ne 0 would yield sth like (argc !=atoi("0")).Of course this holds only for shell where test/[ is a builtin, which
is the at least the case for zsh, bash, and dash (but not posh).
Quoted 7 lines
The reason we used "case" is that this has always been a builtin (has to be, because it changes workflow).
Therefore I am somewhat uneasy that the patch went in so easily, especially given a message that flies in the face of our endeavours to make git less dependent on any given shell (as long as it is not broken to begin with).
The comment "... holds only for a shell where [ is a builtin" doesn't make any sense to me though: "-ne" is a standard operator even if "[" isn't a builtin. It's useful if you are testing numbers in potentially non-canonical form, e.g., with leading zeroes or something, and AFAIK is quite portable.
-Miles
Signature · 1 line
Everywhere is walking distance if you have the time. -- Steven Wright
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Miles,
Quoted 10 lines · Miles Bader
Quoted 7 lines
The reason we used "case" is that this has always been a builtin (has to be, because it changes workflow).
Therefore I am somewhat uneasy that the patch went in so easily, especially given a message that flies in the face of our endeavours to make git less dependent on any given shell (as long as it is not broken to begin with).
The comment "... holds only for a shell where [ is a builtin" doesn't make any sense to me
The 'while case ...' construct does not invoke any external commands. The 'while test ...' too, but only when 'test' is builtin. When 'test' is the external binary you get one additional fork/exec per each cycle.
I believe that this trick comes from the old days where people were generally much more eager to save CPU cycles than now ;)) But if you tried to run Cygwin on the moderately new machines (like PIII), you still can notice that commands like 'man' are a bit slow just because they require a bunch of preprocessors (and processes in pipe) to run. So, "save the process, kill the fork/exec" ;))
Quoted 4 lines
though: "-ne" is a standard operator even if "["
isn't a builtin. It's useful if you are testing numbers in potentially non-canonical form, e.g., with leading zeroes or something, and AFAIK is quite portable.
In general -- yes, it is portable and good for leading zeros. But in the case of $#, there will hardly be leading zeros or something else. And using '!=' typically saves you one atoi() + some other checks, since it typically translates to a bare strcmp().
Signature · 1 line
Eygene
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Quoted 7 lines · Eygene Ryabinkin
Quoted 2 lines
The comment "... holds only for a shell where [ is a builtin" doesn't make any sense to me
The 'while case ...' construct does not invoke any external commands. The 'while test ...' too, but only when 'test' is builtin. When 'test' is the external binary you get one additional fork/exec per each cycle.
In practice that's not an issue though -- every reasonable shell has test as a builtin these days, so the "works when test is not a builtin" criteria is really important only for robustness.
Quoted 2 lines
I believe that this trick comes from the old days where people were generally much more eager to save CPU cycles than now ;))
Yes. I still occasionally find myself using "case" where if+test might be more natural, but I think it's basically an anachronism these days, and causes more harm by reducing readability than good.
-Miles
Signature · 2 lines
The car has become... an article of dress without which we feel uncertain, unclad, and incomplete. [Marshall McLuhan, Understanding Media, 1964]
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Quoted 4 lines · Miles Bader
Quoted 3 lines · Eygene Ryabinkin
Quoted 2 lines
The comment "... holds only for a shell where [ is a builtin" doesn't make any sense to me
Quoted 8 lines
Quoted 4 lines
The 'while case ...' construct does not invoke any external commands. The 'while test ...' too, but only when 'test' is builtin. When 'test' is the external binary you get one additional fork/exec per each cycle.
In practice that's not an issue though -- every reasonable shell has test as a builtin these days, so the "works when test is not a builtin" criteria is really important only for robustness.
Since the external test is pretty standardized, "works when test is one of various builtins" is actually more important for robustness...
Signature · 1 line
David Kastrup
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
In reply to Miles BaderHi,
Quoted 4 lines · Miles Bader
Quoted 3 lines · Eygene Ryabinkin
Quoted 2 lines
The comment "... holds only for a shell where [ is a builtin" doesn't make any sense to me
Quoted 8 lines
Quoted 4 lines
The 'while case ...' construct does not invoke any external commands. The 'while test ...' too, but only when 'test' is builtin. When 'test' is the external binary you get one additional fork/exec per each cycle.
In practice that's not an issue though -- every reasonable shell has test as a builtin these days, so the "works when test is not a builtin" criteria is really important only for robustness.
AAAAAAAAAAAAAARRRRRGGGHHHHHHHHHHHH!
_Exactly_ the same reasoning can be said about the old code: _every_ reasonable shell can grok the code that used to be there!
<rhetoric-question>
So what exactly was your point again?
</rhetoric-question>
Ciao,
Dscho
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Quoted 6 lines · Johannes Schindelin
Quoted 4 lines · Miles Bader
...
In practice that's not an issue though -- every reasonable shell has test as a builtin these days, so the "works when test is not a builtin" criteria is really important only for robustness.
Quoted 8 lines
AAAAAAAAAAAAAARRRRRGGGHHHHHHHHHHHH!
_Exactly_ the same reasoning can be said about the old code: _every_ reasonable shell can grok the code that used to be there!
<rhetoric-question>
So what exactly was your point again?
</rhetoric-question>
The points are:
(1) The code used to be there is known to cause trouble with a deployed shell on FreeBSD of some vintage. It may be true that the shell is broken, but it does not matter much to the end user on such systems if breakage is in shell or in scripts --- the end result is that the user cannot benefit from git, and we already happen to know the workaround, which does not make the scripts less readable nor less portable;
(2) It's not like people who work on git scripts share the exact same style and tradition. While I do not personally think there is much readability improvements between the old code and the new code, if more people find the latter easier to work with, it's better to switch to the new code especially because there is no downside.
(2-a) Nobody finds the latter less readable nor impossible
to work with. Even I am not saying that; I only said I do not think it improves.
(2-b) git is not an educational project. It can be done
elsewhere in a UNIX history class, not here, to teach people that "case ... esac" used to be much more preferred over "test" because often the latter was not built-in and slower.
Regarding "$# != 0" vs "$# -ne 0", I agree with the patch by David. If the variable were "$something_else", then it might have been better to use the explicitly numeric form, but I think any seasoned shell people is much more used to see $# and $? (or $status after an earlier "status=$?") compared with numeric string with "=" or "!=".
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
In reply to Johannes SchindelinQuoted 13 lines · Johannes Schindelin
Hi,
Quoted 9 lines · Miles Bader
Quoted 7 lines · Eygene Ryabinkin
Quoted 2 lines
The comment "... holds only for a shell where [ is a builtin" doesn't make any sense to me
The 'while case ...' construct does not invoke any external commands. The 'while test ...' too, but only when 'test' is builtin. When 'test' is the external binary you get one additional fork/exec per each cycle.
Quoted 8 lines
Quoted 3 lines
In practice that's not an issue though -- every reasonable shell has test as a builtin these days, so the "works when test is not a builtin" criteria is really important only for robustness.
AAAAAAAAAAAAAARRRRRGGGHHHHHHHHHHHH!
_Exactly_ the same reasoning can be said about the old code: _every_ reasonable shell can grok the code that used to be there!
There are no known shells that would not grok the proposed code, and the BSD shells don't grok the "code that used to be there".
So what point is there in preserving compatibility with some mystical non-specified shell while breaking compatibility with actually existing shells?
And by using a less human-readable idiom, to boot?
Signature · 1 line
David Kastrup
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
In reply to Johannes SchindelinQuoted 8 lines · Johannes Schindelin
Quoted 3 lines
In practice that's not an issue though -- every reasonable shell has test as a builtin these days, so the "works when test is not a builtin" criteria is really important only for robustness.
AAAAAAAAAAAAAARRRRRGGGHHHHHHHHHHHH!
_Exactly_ the same reasoning can be said about the old code: _every_ reasonable shell can grok the code that used to be there!
As has been stated, that's not true. Some "real" shells don't handle the old code correctly (and the old code is less readable as well).
AFAIK, the new code works all cases, it's merely very slightly slower in unusual circumstances.
-Miles
Signature · 4 lines
/\ /\
(^.^)
(")")
*This is the cute kitty virus, please copy this into your sig so it can spread.Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
In reply to Pierre HabouzitQuoted 14 lines · Pierre Habouzit
Quoted 13 lines · David Kastrup
Quoted 11 lines · David Symonds
Quoted 9 lines · David Kastrup
Quoted 5 lines · Mike Hommey
Review
Quoted 2 lines · David Kastrup
(quoted diff) +1
-1-while case $# in 0) break ;; esac +while test $# != 0Wouldn't -ne be better ?
Why?
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines
Because -ne does a numeric comparison, != does a string comparison, and it's a numeric comparison happening, semantically speaking.
I don't see the point in converting $# and 0 into numbers before comparing them. "!=" is quite more readable, and the old code also compared the strings.
Fwiw $# already is a number.
It isn't.
Quoted 1 line
Hence test $# -ne 0 is definitely a better test.
/* TEST/[ builtin. */
inttest_builtin (list)
WORD_LIST *list;
{
char **argv;
int argc, result;
/* We let Matthew Bradburn and Kevin Braunsdorf's code do the
actual test command. So turn the list of args into an array of strings, since that is what their code wants. */
if (list == 0)
{
if (this_command_name[0] == '[' && !this_command_name[1])
{
builtin_error ("missing `]'");
return (EX_BADUSAGE);
}
return (EXECUTION_FAILURE);
}argv = make_builtin_argv (list, &argc); result = test_command (argc, argv); free ((char *)argv);
return (result);
}
Quoted 4 lines
$# != 0 would yield sth like (
strcmp(sprintf("%d", argc), "0")) $# -ne 0 would yield sth like (argc !=atoi("0")).Not that it matters much, but the latter looks better to me.
Not to me. The code does not support your argument, and all $x expansions certainly are strings, according to manual and usage. I will rework the patch this evening in order to get a commit message more placable to Junio, and I will at his request remove all of the (redundant) quoting. But removing quoting from $# does not turn it into a number: it remains the same string '0'. If someone else feels he should replace all "=" and "!=" tests for "numeric" comparisons with the unreadable numeric tests, he can go ahead proposing a separate patch that should not just cover $#.
You can have bash declare numeric variables, but even they are strings (they just auto-evaluate on assignment):
declare -i nonsense
nonsense="3+$(echo 4)"
echo "$nonsense"
gives 7, even though everything has been "strings" here.
Signature · 1 line
David Kastrup
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
In reply to David KastrupQuoted 14 lines · David Kastrup
A lot of shell scripts contained stuff starting with
while case "$#" in 0) break ;; esac
and similar. I consider breaking out of the condition instead of the body od the loop ugly, and the implied "true" value of the non-matching case is not really obvious to humans at first glance. It happens not to be obvious to some BSD shells, either, but that's because they are not POSIX-compliant. In most cases, this has been replaced by a straight condition using "test". "case" has the advantage of being faster than "test" on vintage shells where "test" is not a builtin. Since none of them is likely to run the git scripts, anyway, the added readability should be worth the change.
Quoted 8 lines
A few loops have had their termination condition expressed differently.
Signed-off-by David Kastrup <[email protected]>---
Ok, this is not really what we have been talking about except in one case, but I think it is actually more of an improvement.
Gaah, didn't I say I do NOT think it is an improvement?
Quoted 2 lines
I consider breaking out of the condition instead of the body od the loop ugly,
Well, as we all know that we disagree on this point, stating what you consider one-sidedly here is quite inappropriate.
Quoted 3 lines
and the implied "true" value of the
non-matching case is not really obvious to humans at first glance.
It is more like "if you do not know shell".
In other words, I am somewhat disgusted with the first part of your proposed commit log message, although I like what the patch does ;-).
contrib/examples/git-tag.sh username= · line 20
Quoted 5 lines
contrib/examples/git-tag.sh +1
-1-while case "$#" in 0) break ;; esac +while test "$#" != 0 do case "$1" in -a)
And let's not quote "$#".
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Quoted 6 lines · Junio C Hamano
Quoted 2 lines · David Kastrup
Ok, this is not really what we have been talking about except in one case, but I think it is actually more of an improvement.
Gaah, didn't I say I do NOT think it is an improvement?
Ah, but I am not presuming to speak for you in my commit message and postings.
Quoted 5 lines
Quoted 2 lines
I consider breaking out of the condition instead of the body od the loop ugly,
Well, as we all know that we disagree on this point, stating what you consider one-sidedly here is quite inappropriate.
Hm. If I create a patch after you basically said "go ahead, I don't mind, but I consider it unimportant", how am I going to put the motivation for the patch in the commit message while expressing _your_ opinion? I thought that using "I" to make clear that it is my personal view would be doing that.
So what am I supposed to write instead?
"There is no good reason for this patch, but we might as well do it."?
Quoted 4 lines
Quoted 2 lines
and the implied "true" value of the non-matching case is not really obvious to humans at first glance.
It is more like "if you do not know shell".
It is more to take in. Believe me, I do know shell.
Quoted 3 lines
In other words, I am somewhat disgusted with the first part of your proposed commit log message, although I like what the patch does ;-).
Could you propose a commit message that would be acceptable to you, yet not make it appear like a mistake to actually commit the patch?
Quoted 7 lines
contrib/examples/git-tag.sh username= · line 20
Quoted 5 lines
contrib/examples/git-tag.sh +1
-1-while case "$#" in 0) break ;; esac +while test "$#" != 0 do case "$1" in -a)And let's not quote "$#".
I kept this as it was originally. Some authors prefer to quote every shell variable as a rule in order to avoid stupid syntactic things happening. Of course, $# never needs quoting, but I did not want to change the personal style of the respective authors. I can make this consistent if you want to.
Signature · 1 line
David Kastrup, Kriemhildstr. 15, 44793 Bochum
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Quoted 15 lines · David Kastrup
Quoted 2 lines · Junio C Hamano
Well, as we all know that we disagree on this point, stating what you consider one-sidedly here is quite inappropriate.
Hm. If I create a patch after you basically said "go ahead, I don't mind, but I consider it unimportant", how am I going to put the motivation for the patch in the commit message while expressing _your_ opinion? I thought that using "I" to make clear that it is my personal view would be doing that.
So what am I supposed to write instead?
"There is no good reason for this patch, but we might as well do it."?
[...]
Quoted 15 lines
Quoted 3 lines
In other words, I am somewhat disgusted with the first part of your proposed commit log message, although I like what the patch does ;-).
Could you propose a commit message that would be acceptable to you, yet not make it appear like a mistake to actually commit the patch?
Quoted 7 lines
contrib/examples/git-tag.sh username= · line 20
Quoted 5 lines
contrib/examples/git-tag.sh +1
-1-while case "$#" in 0) break ;; esac +while test "$#" != 0 do case "$1" in -a)And let's not quote "$#".
Quoted 5 lines
I kept this as it was originally. Some authors prefer to quote every shell variable as a rule in order to avoid stupid syntactic things happening. Of course, $# never needs quoting, but I did not want to change the personal style of the respective authors. I can make this consistent if you want to.
It seems like the window of opportunity to fix the objectable commit message has closed for me, as well as doing the work of removing the "$#" (which you did already): I find that the patch has already made it into upstream.
I am somewhat taken aback that a commit message considered offensive (though I still have a problem understanding why and certainly did not intend this) has been committed into master without giving me a chance to amend it.
Unfortunately, the ensuing discussion around the _technical_ merits is somewhat lopsided since Dscho keeps me in his killfile, and so the commit message in the repository is all he'll ever be able to see from me concerning this matter.
Which makes it more unfortunate that I have not been able to amend it.
Too bad.
Signature · 1 line
David Kastrup
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Quoted 4 lines · David Kastrup
I am somewhat taken aback that a commit message considered offensive (though I still have a problem understanding why and certainly did not intend this) has been committed into master without giving me a chance to amend it.
Heh, that's simple. I changed my mind ;-)
When A and B test for preconditions, and C, D, and E are operations with error reports as their side effects, we can write our loop in these forms:
(1) while A && B && C && D && E || false; do :; done (2) while A && B && C && D && E || break; do :; done (3) while A && B; do C && D && E || break; do :; done
(4) while :; do A && B && C && D && E || break; done
and all of them are equivalent.
But obviously the only sane version is (3).
If your complaint were against things like (1) and (2), I would have completely agreed with you. If you want "effects", you do so between do and done. Although you can use break between do and done if you need to conditionally break out of the loop after causing some effect there, between while and do is where you are only supposed to decide if you want to break out of the loop without causing "effects".
But what you were complaining about was different.
If we were to ignore broken shells that do not return success from a case statement with no matching pattern, the following two are equivalent:
while case "$sth" in foo) break ;; esac; do ...; done while case "$sth" in foo) false ;; esac; do ...; done
Their "case" are used to decide if you want to break out of the loop; the former is (1) being a bit more explicit, and (2) used to be a bit more efficient when false was not built-in.
Now the latter reason is mostly historical and it is not a valid reason to choose the former over the latter anymore. But that does not make it any more confusing than the latter to a person who knows what "break" means in a loop. An explicit 'break' is still more, eh,... explicit ;-)
But the "break" never was the issue. Return value of "case" was.
The reason I took your patch and proposed commit log message (almost) as-is was because you rewrote "case" to "test". That IS an improvement, especially in the presense of a shell in the field that does not implement case statement correctly, and you talk about that in the later part of the commit log message.
The only "offending" part was "I consider...ugly", which is your opinion but I think you as the patch author deserve to express that. I do not think it would not have helped the FreeBSD shell a bit if you removed that "ugliness" by merely replacing "break" with "false", so I think the comment was not just offending but irrelevant, though.
All the rest of your commit message is correct. The spec of "case" might not be obvious to everybody that it ought to return success when no pattern matched. And I found your wording to fold the bug decription of some BSD shells there amusing ;-)
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Quoted 13 lines · Junio C Hamano
Quoted 4 lines · David Kastrup
I am somewhat taken aback that a commit message considered offensive (though I still have a problem understanding why and certainly did not intend this) has been committed into master without giving me a chance to amend it.
Heh, that's simple. I changed my mind ;-)
When A and B test for preconditions, and C, D, and E are operations with error reports as their side effects, we can write our loop in these forms:
Quoted 8 lines
(1) while A && B && C && D && E || false; do :; done (2) while A && B && C && D && E || break; do :; done (3) while A && B; do C && D && E || break; do :; done(4) while :; do A && B && C && D && E || break; done
and all of them are equivalent.
But obviously the only sane version is (3).
Uh, it is the only version with a syntax error.
Quoted 9 lines
If your complaint were against things like (1) and (2), I would have completely agreed with you. If you want "effects", you do so between do and done. Although you can use break between do and done if you need to conditionally break out of the loop after causing some effect there, between while and do is where you are only supposed to decide if you want to break out of the loop without causing "effects".
But what you were complaining about was different.
Basically
while A && B || break; do C && D && E || break; done
Quoted 10 lines
If we were to ignore broken shells that do not return success from a case statement with no matching pattern, the following two are equivalent:
while case "$sth" in foo) break ;; esac; do ...; done while case "$sth" in foo) false ;; esac; do ...; done
Their "case" are used to decide if you want to break out of the loop; the former is (1) being a bit more explicit, and (2) used to be a bit more efficient when false was not built-in.
As a completely irrelevant side note: the autoconf documentation mentions that "false" is more portable than "true" since calling it returns a non-zero exit status even when it is not installed or built-in.
Quoted 7 lines
Now the latter reason is mostly historical and it is not a valid reason to choose the former over the latter anymore. But that does not make it any more confusing than the latter to a person who knows what "break" means in a loop. An explicit 'break' is still more, eh,... explicit ;-)
But the "break" never was the issue. Return value of "case" was.
I guess this has been a misunderstanding: for me, personally, the break was the issue: I don't like breaking out of a condition, since breaking for me is an action. I just used the fact that the BSD shells happen not to grok the constructs (and actually through a somewhat similar confusion between condition and action) to leverage my dislike of this construct and propose a patch.
Quoted 2 lines
The reason I took your patch and proposed commit log message (almost) as-is was because you rewrote "case" to "test".
Uhm, ok. It was a case of realizing "hm, this does not really look much nicer" before I chose to switch to "test". In fact, there is one case statement remaining which I rewrote in the previously discussed manner, and it did not strike me as being much prettier. So maybe I somewhat misjudged the core of my offended sense of aesthetics, but the impetus of the discussion still carried into the commit message.
Alea iacta est ("The SHA-1 has been established").
Signature · 1 line
David Kastrup, Kriemhildstr. 15, 44793 Bochum
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Quoted 4 lines · David Kastrup
As a completely irrelevant side note: the autoconf documentation mentions that "false" is more portable than "true" since calling it returns a non-zero exit status even when it is not installed or built-in.
Ah, I like that ;-) It is obvious when you think about it, and it is so true but in a very twisted way...
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
Quoted 13 lines · Junio C Hamano
Quoted 5 lines · David Kastrup
As a completely irrelevant side note: the autoconf documentation mentions that "false" is more portable than "true" since calling it returns a non-zero exit status even when it is not installed or built-in.
Ah, I like that ;-) It is obvious when you think about it, and it is so true but in a very twisted way...
You mean, it is not false but in a twisted way, don't you?
Signature · 1 line
Do not meddle in the internals of kernels, for they are subtle and quick to panic.
Re: [PATCH] Supplant the "while case ... break ;; esac" idiom
In reply to Junio C HamanoHi,
Quoted 9 lines · Junio C Hamano
Quoted 4 lines · David Kastrup
As a completely irrelevant side note: the autoconf documentation mentions that "false" is more portable than "true" since calling it returns a non-zero exit status even when it is not installed or built-in.
Ah, I like that ;-) It is obvious when you think about it, and it is so true but in a very twisted way...
But would you not have to redirect stderr to /dev/null, then?
In the same vein, we could replace "true" by "! false".
That's such a good idea that I'll go and make a patch.
Ciao,
Dscho
Quoted 10 lines · Eygene Ryabinkin
Quoted 9 lines
That is, what does the shell say if you do this?
case Ultra in Super) false ;; Hyper) true ;; esac && echo case returned ok
Quoted 2 lines
It says 'case returned ok', so I will try to understand why it works here and does not work in the 'while' construct.
What you actually need to do is
false
case Ultra in
Super)
false ;;
Hyper)
true ;;
esac && echo case returned ok
Signature · 1 line
David Kastrup
Quoted 17 lines · David Kastrup
Quoted 10 lines · Eygene Ryabinkin
Quoted 9 lines
That is, what does the shell say if you do this?
case Ultra in Super) false ;; Hyper) true ;; esac && echo case returned okQuoted 2 lines
It says 'case returned ok', so I will try to understand why it works here and does not work in the 'while' construct.
What you actually need to do is
Quoted 7 lines
false
case Ultra in
Super)
false ;;
Hyper)
true ;;
esac && echo case returned ok
AHHHHHH.
Is "case" supposed to be transparent?
Quoted 27 lines · * Junio C Hamano ([email protected]) [070921 23:58]:
Quoted 17 lines · David Kastrup
Quoted 10 lines · Eygene Ryabinkin
Quoted 9 lines
That is, what does the shell say if you do this?
case Ultra in Super) false ;; Hyper) true ;; esac && echo case returned okQuoted 2 lines
It says 'case returned ok', so I will try to understand why it works here and does not work in the 'while' construct.
What you actually need to do is
Quoted 7 lines
false
case Ultra in
Super)
false ;;
Hyper)
true ;;
esac && echo case returned ok
Quoted 3 lines
AHHHHHH.
Is "case" supposed to be transparent?
That doesn't seem to be the case (no pun intended) on either bash or dash. Here's what I tested on bash (apologies for the long lines; these are verbatim pastes from my shell):
vineet@sprocket:~$ false vineet@sprocket:~$ case Super in Super) echo super ; false ;; Hyper) echo hyper ; true ;; esac && echo case returned ok super vineet@sprocket:~$ false vineet@sprocket:~$ case Hyper in Super) echo super ; false ;; Hyper) echo hyper ; true ;; esac && echo case returned ok hyper case returned ok vineet@sprocket:~$ false vineet@sprocket:~$ case Ultra in Super) echo super ; false ;; Hyper) echo hyper ; true ;; esac && echo case returned ok case returned ok vineet@sprocket:~$
and on dash:
vineet@sprocket:~$ dash $ false $ case Super in Super) echo super ; false ;; Hyper) echo hyper ; true ;; esac && echo case returned ok super $ false $ case Hyper in Super) echo super ; false ;; Hyper) echo hyper ; true ;; esac && echo case returned ok hyper case returned ok $ false $ case Ultra in Super) echo super ; false ;; Hyper) echo hyper ; true ;; esac && echo case returned ok case returned ok $
So it seems like a "case" statement isn't special; it returns a status like any other statement.
Vineet
Signature · 1 line
http://www.doorstop.net/
Quoted 27 lines · Junio C Hamano
Quoted 17 lines · David Kastrup
Quoted 10 lines · Eygene Ryabinkin
Quoted 9 lines
That is, what does the shell say if you do this?
case Ultra in Super) false ;; Hyper) true ;; esac && echo case returned okQuoted 2 lines
It says 'case returned ok', so I will try to understand why it works here and does not work in the 'while' construct.
What you actually need to do is
Quoted 7 lines
false
case Ultra in
Super)
false ;;
Hyper)
true ;;
esac && echo case returned ok
Quoted 3 lines
AHHHHHH.
Is "case" supposed to be transparent?
Not that I would know. It is basically a revival of the
false
if false then : ; fi || echo "this fails!?!"
bug that probably has been fixed by now. For obvious reasons, conditionals without a taken branch are considered to have an exit code of 0.
Signature · 1 line
David Kastrup, Kriemhildstr. 15, 44793 Bochum