5

I have written one simple function in shell that returns 0 or 1 based on some condition.Let me call that function name foo

foo(){

...

...

}

Now i am trying to call foo in if condition as follow:-

if ( foo $1 )

...

..

It works fine.But when i used follow approach to call ,then i get error

if [ foo $1 ]

...

...

Why does it throws error as "Unary operator expected"?

  • 3
    Note that the first argument to if is a command -- [ is the test command, not mere syntax. if uses the exit status of the command (or pipeline) to determine "true/false". – glenn jackman May 3 '18 at 20:15
5

When you use:

if ( foo $1 )

You are simple executing foo $1 in a subshell and if is acting on it's exit status.

When you use:

if [ foo $1 ]

You are attempting to use the shell test and foo is not a valid test operator. You can find the valid test operators here.


It's not necessarily relevant for your issue but you should also always quote variables especially inside the shell test brackets. The shell test will succeed simply with the presence of something. So even when using a valid test operator you could get unwanted results:

$ unset var
$ [ -n $var ] && echo yes
yes
$ [ -n "$var" ] && echo yes
$ [ -n "" ] && echo yes
$ [ -n ] && echo yes
yes
$ [ foo ] && echo yes
yes
$ [ foo bar ] && echo yes
-bash: [: foo: unary operator expected

The presence of a single string inside the shell test will evaluate to true where the presence of two or more strings expects that one of them be a valid test operator.

  • In second case of sqaure braces,is there any way we can compare return type of foo with 0 ? i tried :- if [ foo $1 -eq 0 ] but it throws error.So for such cases always we should use round braces? – rahul sharma May 3 '18 at 20:18
  • If [ foo $1 ] is going to return string, i.e. echo back something, you might need [ "$(foo "$1")" = "some expected output" ]. The plain [ foo $1 ] will produce unary operator expected in bash, and POSIX test. – Sergiy Kolodyazhnyy May 3 '18 at 20:19
  • Understood..... – rahul sharma May 3 '18 at 20:24
  • @ Sergiy Kolodyazhnyy : The way you suggested to check the string with some expected output.Now if i want to use this thing to check if my function has returned 0 or 1 ,like in my above comment, if [ $"(foo $1)" -eq 0] then success else fail fi. Is this valid thing or not because i get unary operator expected error – rahul sharma May 3 '18 at 20:39
  • @rahulsharma Why use [ if you want to check the return code? Just do if foo $1. The syntax $"(foo $1)" is wrong if you want to do subprocess substitution. That's just going to return the string (foo $1) verbatim. The correct syntax is "$(foo $1)". Now, if [ "$(foo $1)" -eq 0 ] only makes sense if foo $1 prints 0 like echo 0 would print 0. – JoL May 4 '18 at 0:17
3

if statements deal with exit status of commands. Your function should either return exit status or echo back a string. For your purpose, return seems more suitable. Return 0 for successful completion of function, and anything else if error occured. Example:

$ foo(){ [ -e '/etc/passwd' ] && return 0;  }
$ if foo; then echo "/etc/passwd exists"; fi
/etc/passwd exists

In fact, it should be noted that what you often see as if [ ... ]; then... is exactly the same as if test ...; then... because [ and test are the same command and return zero or non-zero exit status to indicate if error occured.

  • The "return 0" is not needed in the function. – Kusalananda May 3 '18 at 20:43
  • @Kusalananda For successful exit status, yes, not necessary, we can just do return. But that's just an example. – Sergiy Kolodyazhnyy May 3 '18 at 21:30
3

Just in addition to what other users said, the actual syntax of the if compound command is:

if compound_list
then compound_list
[elif compound_list
then compound_list]...
[else compound_list]
fi

Where compound_list is, basically, a list of any number of commands. if will check the exit code of the last command of the first COMPOUND_LIST to decide what to execute (the then ..., one of the elif ...; then ... or the else ...).

That means that you can rewrite it like this:

if foo "$1"; then
  # Code to execute if foo returns 0
else
  # Code to execute if foo returns 1
fi

If foo is able to return many other status (2, 3, ..., 254, 255), then using case would be better:

foo "$1"

case "$?" in
  0) # Code to execute if foo returns 0 ;;
  1) # Code to execute if foo returns 1 ;;
  2) # Code to execute if foo returns 2 ;;
  3) # Code to execute if foo returns 3 ;;
  ...
  254) # Code to execute if foo returns 254 ;;
  255) # Code to execute if foo returns 255 ;;
esac

Edit 1

is ";" after "$1" is defined in syntax?

Yes and, as Kusalananda stated, it's used as command delimiter.

POSIX defines the following commands:

  • Simple command: [assignments] program [arguments] [redirections]
  • Pipeline: [!] command [pipe_operator command]...
  • List:
    • AND-OR list: pipeline [and-or_list_operator pipeline]...
    • Compound list: and-or_list [compound_list_operator and-or_list]
  • Compound command:
    • Grouping commands:
      • ( compound_list )
      • { compound_list; }
    • For: for name [in words]; do compound_list; done
    • Case: case word in [[(] patterns ) compound_list ;;]... esac
    • If: if compound_list; then compound_list; [elif compound_list; then compound_list;]... [else compound_list;] fi
    • While: while compound_list; do compound_list; done
    • Until: until compound_list; do compound_list; done
  • Function definition command: name() compound_command [redirections]

A compound_list_operator could be either a semicolon or a newline and it's used in a compound_list/for/case/if/while/until context.

Note that a semicolon is also required when in the { compound_list; } command the last command of compound_list and the closing bracket } are in the same line.

  • is ";" after "$1" is defined in syntax? I have been running some simple programs but have not used semicolon anywhere – rahul sharma May 3 '18 at 20:27
  • 1
    @rahulsharma You may replace that semicolon wit a newline. Both semicolons and newlines are command delimiters. – Kusalananda May 3 '18 at 20:44
  • @rahulsharma Yes. Answer updated. – nxnev May 3 '18 at 21:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.