3

I am assuming the following block represents a function, but it may not:

mounted()true
if
  ...
fi ... && mounted

I want to understand what that construct is. I did not find similar syntax under the functions heading at zsh

A more complete snippet of the actual code is:

#! /bin/zsh -p

# leaving out a section...

tmpdir=$(mktemp -d) || exit

mounted()true
if
  mount "$type[@]" -o "${(j[,])opts}" -- "$dev" "$tmpdir"
then
  mount --bind -- "$tmpdir/$subdir" "$dest" || mounted()false
  umount -- "$tmpdir"
fi && rmdir -- "$tmpdir" && mounted

Once I understand it, I am going to convert the entire zsh script to a language I am more familiar with. I might convert it to bash as an intermediate step.

1
  • 1
    A good way to figure out what something defined in the shell is is to call type name. It would have told you that it was a shell function. With -f you would see its content.
    – Braiam
    Nov 16 at 19:58
11

That's Bourne shell syntax (from the 80s) to define a function, it's not specific to zsh.

In the Bourne shell, a function is defined by sticking functionName() in front of a command¹.

So name() true defines a function called name with the true simple command as its body.

$ name()true
$ type name
name is a shell function

That's the case in almost every Bourne-like shell (ksh, ash, dash, bosh, pdksh, mksh, zsh...). A notable exception is bash² (the GNU shell) which initially only allowed a commmand group ({ ...; }) to be the body of a function and later changed it to any compound command as POSIX requires for the sh language.

So, in bash, you'd need name()((1)) or name()[[ . ]] ((( ... )) and [[ ... ]] —constructs borrowed from ksh— being considered compound commands there), or name() { true; } where the body of the function is a command group with only one simple command inside.

Note that ksh was the shell that first introduced functions, though with a different syntax: function name { body; }. zsh supports both the Korn and Bourne syntax and has extensions of its own.

In zsh see info zsh function, which should lead you to the part about the Korn function keyword for the syntax of function definitions. One extension of zsh is that you can define more than one function at once with the same body, and use any string as the function name³:

vrai 1 + $'\1' () true
faux 0 - $'\0' '' () false

To define a few different functions that call true or false without arguments.

If you omit the function name, that becomes an anonymous function, which can take arguments and is invoked on the spot:

() { echo There are $# non-hidden txt files; } *.txt(N)

(though for obvious reasons, for that anonymous function to be able to take arguments, its body can't be a simple command).

In zsh, functions are also made available via the $functions special associative array where the keys are the function names, and value the code in the body. So functions[name]=true is another way to define the name function.


Now, using functions to store a boolean value is not something you see very often, but if you stop to think about it for a minute, it does make quite a lot of sense.

In the C language for instance, if / while constructs or the && / || logic operators act on numbers. if (condition) something does something if condition is a non-zero number. awk or perl which are C-like languages extend that to condition being defined or being a non-empty strings.

But shells are before all command-line interpreters. In shell, all is a command. if / while and && / || in most shells act on commands. if condition; then something; fi does something if the condition command succeeds.

false and true are boolean constant commands (builtin in most shells) that always fail / succeed respectively, so they are the obvious ones to represent boolean values. And functions are the most appropriate data structure to store commands (or shell code in general).

aliases (which many consider a broken heritage from csh, a shell which (like the Bourne shell initially) didn't have functions) wouldn't work here as aliases are expanded at the time the code containing them is read, not at runtime. For instance in:

alias name=false
myfunction()
  if name; then
    something
  fi
alias name=true
myfunction

The body of the function will actually contain if false; then... because the name alias was expanded at the time the code to define the function was read, not at the time the function runs.

One could store the code in a variable instead of a function:

name=true name=false
if eval " $name"; then
  ...
fi

Where we test for success of the eval command which is told to interpret the code in $name4. Note that in that case an empty / undefined (except with the nounset option) $name yields true.

Or we could do (and that's what I generally do in sh/bash scripts):

name=true name=false
if "$name"; then
  ...
fi

Where we run the command whose name is stored in $name without arguments.

Or:

name=(true) name=(false)
if "${name[@]}"; then
  ...
fi

Where we store the arguments of a simple command in a $name array variables.

People used to C-like languages may want to store the booleans as integer variables and run a command such as [ / test or expr to test the value of integers when converted from their textual representations:

false=0 true=1
name=$false name=$true

if expr "$name" > /dev/null; then
  ...
fi
if [ "$name" -ne 0 ]; then
  ...
fi

In Korn-like shells (includes bash and zsh), you can use the ((...)) construct that evaluates C-like arithmetic expressions and returns success if that yields a number other than zero (even NaN).

false=0 true=1
name=$false name=$true
# or even:
name=false name=true
if (( name )); then
  ...
fi

You could also run a command that compares strings (like [/test/expr again), or does pattern matching like that other Korn-like [[ string = pattern ]] construct:

name=true name=false
if [ "$name" = true ]; then
  ...
fi

(that one sounds as alien to me as doing a if (strcmp(name, "true") == 0)... in C).

Or even if you fancied, with a awk/perl-like test for a defined variable.

unset -v name # unset = false
name=         #   set = true
if [ -n "${name+true}" ]; then
  ...
fi

¹ There was a bug in the Bourne shell (not in its clones/derivatives5) though where that didn't work properly if you used a simple command with redirections as the body of the function, which is probably why POSIX only requires compound commands be supported as body of functions.

² yash (written to the POSIX specification), posh (based on pdksh but written to help verify compliance to the standard so with most extensions to the standard removed including that one) are two other exceptions

³ that's consistent with the fact that external commands and command arguments can be any string as files can contain any string (though a filename can't be empty and filenames/arguments can't contain NUL bytes). In the Bourne shell, function and variable names shared the same namespace (you couldn't define a function and variable with the same name) and function names had the same limitations as variable names.

4 with a space prepended for correctness as some eval implementations don't support -- to mark the end of options

5 zsh, itself used to have its own problems when redirections where used in the body of a function, most of which have been fixed. But even now, and as explicitly stated in the documentation, in f() { cmd; } < $1, (in the special case where the body is a command group with redirections) that $1 does not refer to the $1 in the scope of the function, but to the $1 of the caller which in that instance makes it non-POSIX-compliant. That does not apply to simple commands or other types of compound commands (which zsh internally wraps in {...}).

6
  • 1
    It would be good to tie this back to the original example - why would someone define a function whose only action was to run the true built-in? It looks like the example is using it as a weird kind of variable - conditionally re-defining it to run false later. Is this to do with variable scope? Or the non-availability of boolean values?
    – IMSoP
    Nov 16 at 16:07
  • @IMSoP, functions are the obvious data types in which to store code. true and false are boolean commands. Shells run commands, that's what they do. In shells, you do if cmd. Compare with C where you do if (number). In C, you do boolean = 1; if (boolean). In shell, you do: boolean()true; if boolean. Nov 16 at 16:52
  • 1
    Be careful of over-estimating what is obvious to others. In most programming paradigms, "storing code" and "boolean commands" are not common-place concepts, nor are "functions" generally treated as containers to be re-defined based on the state of the program. I can see how it works, and that it might be a common idiom among shell experts, but it would never have occurred to me to use them that way. Hence I think the answer would be better if it explained that idiom, and thus the example in the question, before delving into the weeds of exotic function names.
    – IMSoP
    Nov 16 at 16:57
  • @IMSoP, I'll try and fold that into the answer. Nov 16 at 16:59
  • 1
    type name is a generic way to identify stuff created within zsh.
    – Braiam
    Nov 16 at 19:59

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.