A function definition is a command. When a function definition is run, I thought that the function body would be kept intact, as if the function body is single quoted.

I knew I was wrong, when I understood the following from the Bash manual in G-Man’s answer to my Alias and functions question:

Aliases are expanded when a function definition is read, not when the function is executed, ….

So alias expansion is performed in the function body, while parameter expansion is not, as shown by the following example:

$ alias myalias=cat
$ echo $var
3
$ myfunc() { myalias myfile; echo $var; }
$ declare -fp myfunc
myfunc () 
{ 
    cat myfile;
    echo $var
}

Similarly, a call to a function is also a command.

aliases defined in a function are not available until after that function is executed.

so alias definition is executed only when executing, i.e., calling the function, not when running the function definition.

alias expansions and alias definition execution are just two of the shell operations listed below.

My questions are:

  1. What shell operations are performed inside the function body, and what operations are not performed,

    • when running a function definition?

    • when calling a function, i.e., executing a function?

  2. Are there any shell operations which are performed during both running the definition of a function and calling the function?

    Or do running the definition of a function and calling the function perform non-overlapping sets of shell operations?

The possible operations are listed in the following quote from the Bash manual:

3.1.1 Shell Operation

The following is a brief description of the shell’s operation when it reads and executes a command. Basically, the shell does the following:

  1. Reads its input from a file (see Section 3.8 [Shell Scripts], page 39), from a string supplied as an argument to the -c invocation option (see Section 6.1 [Invoking Bash], page 80), or from the user’s terminal.

  2. Breaks the input into words and operators, obeying the quoting rules described in Section 3.1.2 [Quoting], page 6. These tokens are separated by metacharacters. Alias expansion is performed by this step (see Section 6.6 [Aliases], page 88).

  3. Parses the tokens into simple and compound commands (see Section 3.2 [Shell Commands], page 8).

  4. Performs the various shell expansions (see Section 3.5 [Shell Expansions], page 21), breaking the expanded tokens into lists of filenames (see Section 3.5.8 [Filename Expansion], page 30) and commands and arguments.

  5. Performs any necessary redirections (see Section 3.6 [Redirections], page 31) and removes the redirection operators and their operands from the argument list.

  6. Executes the command (see Section 3.7 [Executing Commands], page 35).

  7. Optionally waits for the command to complete and collects its exit status (see Section 3.7.5 [Exit Status], page 38).

  • 1
    I would suppose the shell has to do the steps up to 3. when defining a function. Invalid syntax produces an error while the definition is loaded, and in any case, the shell has to be able to tell the brace closing the function from other possible braces inside the function. Expansions obviously aren't done until the function is executed. – ilkkachu Aug 4 '17 at 8:57
  • @ilkkachu, I think you're right about "up to step 3." But alias expansion is of course part of step 2, so is included in that. Simple enough. – Wildcard Aug 9 '17 at 0:23
up vote 2 down vote accepted

<rant>
“running a function definition” and “running the definition of a function” are not common idioms.  I expect that most people would refer to that simply as “defining a function”.  OK, that phrase might be interpreted to refer to

  1. conceptually specifying the characteristics and interfaces (parameters and other inputs, processing, and outputs) of a function (this is an activity performed by a person, or a group of people)
  2. choosing the implementation of a function; i.e., what commands to use in the function body.  This is an activity performed by a person, or a group of people, possibly using paper, whiteboards, and/or blackboards.
  3. typing the implementation of a function into a text editor or directly into an interactive shell (this, of course, is also an activity performed by a person, or possibly a group of people, or possibly a very intelligent and dexterous cat, dog or monkey)

If you fear confusion with the above, you might be better served by phrases like “reading a function definition” and “processing the definition of a function”.

You say, “A function definition is a command.”  It’s not clear what a claim like that even means — it’s a matter of semantics — but, at the semantic level, it’s debatable.  Section 3.2 of the Bash manual (Shell Commands) lists six kinds of shell commands: Simple Commands, Pipelines, Lists, Compound Commands, Coprocesses, and GNU Parallel.  Function definitions don’t fit well into any of those categories.
</rant>

Getting to your question, and looking at the seven steps / operations,

  1. Reading input obviously has to happen before anything else can happen.  It’s, again, a matter of semantics — you could say that reading input from a file or other stream text source is part of “reading a function definition” or that it is a prerequisite / prelude to “processing the definition of a function”.
  2. Breaking the input into words and operators is clearly part of “processing the definition of a function”.
    • Breaking the input into tokens is, of course, a prerequisite / prelude to parsing the tokens (step 3, below).
    • The manual says, “Alias expansion is performed by this step,” and you show in the question that you know that alias expansion happens when the function definition is read.
  3. Parsing the tokens.  This must at least start during the “processing the definition of a function” phase for the shell to realize that it is looking at a function definition and not a simple command.  Beyond that, we can do this simple experiment: type either of the following into a shell:

    myfunc1() {                     myfunc2() {
        <               or              ;
    }                               }
    

    It will fail with a “syntax error” before you get a chance to type the }.  (> and & produce the same effect.)  So clearly this step is part of processing the definition of the function.


  1. Performing the various shell expansions is part of calling the function.
    • You show in the question that you know that parameter expansion does not happen when the function definition is read.
    • It’s equally trivial to demonstrate that pathname / filename expansion does not happen when the function definition is read.  In your example, change echo $var (which should be echo "$var", but that’s another matter) to echo *.
      • If you look at the function definition, you’ll see that it still says echo * and has not been expanded.
      • Change directory (cd) and/or create file(s) and/or delete file(s) and/or rename file(s), and then call (execute) the function.  You’ll see that it outputs the current list of files1 in the current directory, and doesn’t reflect the contents of the directory where/when you defined the function.
  2. Performing redirections is part of calling the function.  This is also trivial to verify.  In your example, change echo $var to echo hello > myfile.
    • If you look at the function definition, you’ll see that it still says  > myfile.
    • Check the directory, and you’ll see that myfile has not been created (yet).
  3. Executing the command — do you really need to ask?
  4. Waiting for the command to complete can’t possibly happen until the command has been executed (step 6), so this obviously is performed only when calling the function.

In other words, exactly what @ilkkachu said.

The one exceptional special case I can think of is that steps 2 and 3 (lexical analysis and parsing) occur (again) when executing a function if the function contains eval statements — but I believe that that’s outside the scope of what your question is really asking.
____________
1 excluding hidden (dot-) files, unless you have said shopt -s dotglob

  • Thanks. Search in the bash manual, you will see "a function definition is itself a command." – Tim Aug 7 '17 at 0:09
  • @Tim, the full quotation makes it abundantly clear what is the important concept being communicated: "Aliases are expanded when a function definition is read, not when the function is executed, because a function definition is itself a compound command. As a consequence, aliases defined in a function are not available until after that function is executed. To be safe, always put alias definitions on a separate line, and do not use alias in compound commands." – Wildcard Aug 9 '17 at 0:21

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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