2

I have defined a function similar to the following (as an alias),

testif() {
    ([ $1 ]) && echo "true" || echo "false"
}

..and then was able to call testit "1 == 2" in bash shell and the output was false as expected. But it is not working in zsh. The output to the same call in zsh is true.

  1. Is it possible to code such a function for zsh?
  2. Why it is giving wrong output in zsh? shouldn't zsh bash compatible?
2
  • 1. zsh is not bash-compatible. 2. it will (scarequotes) "work" in zsh if you change the $1 to $=1. 3. don't ever use a && b || c -- it's not similar to a ? b : c or if a; then b; else c; fi.
    – user313992
    Feb 12, 2021 at 9:04
  • Time to change all my scripts then.. Feb 12, 2021 at 11:37

1 Answer 1

4

What you did doesn't work in bash either except in very simple examples.

$ testif 'hello world = hello world'
bash: [: too many arguments
false
$ testif '"hello world" = "hello world"'
bash: [: too many arguments
false
$ testif '* = *'
(usually false with an error message, but it depends on the files in the current directory)

Even your simple example doesn't work in zsh because unlike other sh-like shells, in zsh, $1 really means “take the value of the first argument” (almost: if the expansion results in an empty word, it's removed altogether). In bash and other sh-like shells, $1 means “take the value of the first argument, split it into words and treat each word as a glob pattern” which is almost never what you want.


You can pass a condition in the syntax of the test/single-brackets command as separate arguments.

testif () {
  if [ "$@" ]; then echo true; else echo false; fi
}

This works (with the limitations of the [ command, e.g. no && and || operators.

$ testif '*' = '*'
true
$ testif hello = hello
true
$ testif 'hello world' = 'hello world'
true
$ testif 0 = 00
false
$ testif 0 -eq 00
true

There's rarely a good reason to pass a [ condition around, though. You might as well pass the whole command. This not only lets you pass other kinds of conditions (e.g. grep … or if systemctl is-active …), but if you need more than a simple command, you can define an auxiliary function.

testif () {
  if "$@"; then echo "true"; else echo "false"; fi
}
$ testif [ 'hello world' = 'hello world' ]
true
$ testif grep -q : /etc/passwd
true

Another approach would be to pass the whole command as a single string, and use eval inside the function.

testif () {
  if eval "$1"; then echo true; else echo false; fi
}
$ testif '[ "hello world" = "hello world" ]'
true
$ testif '[ "hello world" = "h*" ]'
false
$ testif '[[ "hello world" = "h*" ]]'
false
$ testif '[[ "hello world" = h* ]]'
true

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .