Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: simple question on conditional expression



On Mon, Oct 06, 2025 at 09:42:14PM +0100, Stephane Chazelas wrote:
> 2025-10-06 12:55:13 +0200, Andreas Kähäri:
> [...]
> > > if ( echo $ANS | grep B ); then echo OK;fi
> [...]
> > The "if" statement uses the exit status of the command in parentheses to
> > determine whether to execute the "then" block.
> [...]
> 
> That's rather misleading. While there are shells such as (t)csh
> or rc and derivatives where (...) is part of the "if" statement
> syntax, that's not the case of zsh and other Bourne-like shells.

Hi Stéphane,

Thanks for the detailed exposition!  However, it's unfortunate that it
was pivoted off a misunderstanding of my original statement.  I did not
claim that "if" statements use parentheses as part of their syntax.
I said that "The 'if' statement uses the exit status of the command
in parentheses", which is true in the context of the given example
(i.e., the "The 'if' statement" refers to a specific instance of "if"
statements). It would have been clearer if I had said "The 'if'
statement in the example uses the exit status of the command in
parentheses", that's true.

In any case, the examples I showed uses no subshells, which shows
that the parentheses are not part of the syntax of the "if" statement.

I post them again here for clarity:

	if echo "$ANS" | grep -q B; then echo OK; fi

	if grep -q B <<< "$ANS"; then echo OK; fi

These show that the parentheses are not part of the syntax of the "if"
statement.

Now, what I *though* you would comment on, and which I stopped short of
mentioning in a follow-up, is that the use of "grep" in this
particular case is superfluous, and that a pattern match would be
preferable. That might have been useful information for the original
poster.  E.g.,

	if [[ $ANS == *B* ]]; then echo OK; fi

or, portably,

	case $ANS in (*B*) echo OK ;; esac

... unless, of course, the pattern is more complex than what globbing
can handle, in which case using an extended globbing pattern or a
regular expression might be justified (still without "grep", though,
unless it supported a dialect of regular expressions that zsh doesn't
and which is needed).

You could also have commented on the difference between the two ways
passing the string to "grep -q", one of which spawns a subshell, and the
other does not, or that using "echo" is problematic for certain values
of $ANS.

There is also the fact that "grep -q" stops at the first match, which
would be irrelevant in this case, but could be relevant in other.

I'll leave the rest of your message intact below.

Regards,


> 
> The Bourne syntax is:
> 
> if
>   cmdlistA
> then
>   cmdlistB
> else
>   cmdlistC
> fi
> 
> Which runs cmdlistB if cmdlistA succeeds and cmdlistC otherwise.
> 
> (...) in Bourne-like shell is a compound command which is used
> to run what's inside in a subshell environment. It's not related
> in anyway to the "if" statement.
> 
> You do: (umask 077; cd /dir && cmd) for instance for the change
> of umask and working directory to be done *only* in the subshell
> prior to running cmd.
> 
> A (...) compound command exits with the exit status of the last
> command run within, so ( echo $ANS | grep B ) will exit with the
> exit status of grep, but the (...) to request a subshell
> environment are unnecessary here.
> 
> A simple command often used in the cmdlistA part of an "if"
> statement is the "[" command which can be used to perform a
> number of simple tests. "grep" is also often used with the "-q"
> (to not output the matching lines) and sometimes "-s" (to not
> output any error) to look for matching lines in a file as in:
> 
> if
>   grep -qe "$regex" < $file
> then
>   print -r "At least one line matching $regex was found in $file"
> else
>   print No match
> fi
> 
> Other *compound* commands commonly used as the condition part of
> "if" statements are the ((...)) and [[...]] contructs from the
> Korn shell, the former for C-like arithmetic expressions, the
> latter for doing mostly the same as what the "[" command does
> but using a specific microlanguage.
> 
> Here, to test whether a string contains another, you'd use the
> latter:
> 
> if
>   [[ $ANS = *B* ]]
> then
>   print -r -- "$ANS contains B"
> fi
> 
> Or the POSIX equivalent:
> 
> case $ANS in
>   (*B*) printf '%s\n' "$ANS contains B"
> esac
> 
> If you had to use grep, that would have to be:
> 
> if
>   echo -E - $ANS | grep -q B
> then
>   print -r -- "$ANS contains B"
> fi
> 
> (zsh-specific) or:
> 
> if
>   print -r -- "$ANS" | grep -q B
> then
>   print -r -- "$ANS contains B"
> fi
> 
> (Korn-shell compatible) or:
> 
> if
>   printf '%s\n' "$ANS" | grep -q B
> then
>   printf '%s\n' "$ANS contains B"
> fi
> 
> (POSIX). But I can't think of any reason why you would.
> 
> As an extra note, zsh has short forms of the if statement that
> can be used if the last command in cmdlistA is a *compound
> command* such as (( ... )), [[ ... ]] or (...)
> 
> if [[ $ANS = *B* ]] echo match
> 
> or:
> 
> if [[ $ANS = *B* ]] {print match} else {print no match}
> 
> So in the end you can have something looking similar to C:
> 
> if (( 3.1416 < 6.02e23 )) {
>   echo Avogadro beats Pi
> }
> 
> Even more C-like than the C-shell where it would be:
> 
> if (1 < 2) then
>   echo ...
> endif
> 
> -- 
> Stephane

-- 
Matti Andreas Kähäri
Uppsala, Sweden

.




Messages sorted by: Reverse Date, Date, Thread, Author