Search code examples
bashshell

bash error message seemingly comes from environment instead of from bash itself when executing inside a function


Consider the following simple bash script

cd nowhere

This results in an error message something like

my-script.sh: line 1: cd: nowhere: No such file or directory

So far so good. This makes sense. Let's tweak it a bit, by wrapping the cd in a function:

function go_nowhere { cd nowhere; }
go_nowhere

We get the same error message. This still makes sense. Let's try a subshell instead of using a function.

bash -c 'cd nowhere'

Again, similar error message, but now bash "signs" the error message in its own name instead of our script's name:

bash: line 1: cd: nowhere: No such file or directory

So far, everything still makes sense. Now let's do both! We'll wrap cd in a function, and run that function in a subshell:

function go_nowhere { cd nowhere; }
export -f go_nowhere
bash -c 'go_nowhere'

Now are message is a bit different. First, the line number is "line 0" instead of line 1. This makes sense: since we used "export", that means from the perspective of the subshell, the function was defined "before the first line" since it was inherited from its parent. But the other difference is weirder:

environment: line 0: cd: nowhere: No such file or directory

What is environment? I am not running any program called "environment" so why is bash printing an error message that claims to be coming from such a program? Is it trying to tell me that the error is in a function, which bash inherited from the environment? We can test that theory:

function go_nowhere { cd nowhere; }
bash -c "`declare -f`; go_nowhere"

The line number is now bigger again, but the error message is still "signed" as environment:

environment: line 3: cd: nowhere: No such file or directory

Now I really don't get it. It did NOT get this function from the environment. So where is the string environment coming from? Note that this is not specific to scripts... this can be reproduced interactively from the prompt: an error inside a function in a subshell gets reported as from environment and I have no idea why


Solution

  • What is environment?

    It is a hardcoded string in bash source code here: https://github.com/bminor/bash/blob/master/make_cmd.c#L804 .

      /* Assume that shell functions without a source file before the shell is
         initialized come from the environment.  Otherwise default to "main"
         (usually functions being defined interactively) */
      if (temp->source_file == 0)
        temp->source_file = shell_initialized ? "main" : "environment";
    

    Do not use backticks. Prefer $(..). Check your code with shellcheck.

    You can get the other string "main" by reading from stdin. shell_initialized means the shell is ready to read interactive input.

    $ LC_ALL=C bash -s <<<"$(declare -f go_nowhere); go_nowhere"
    main: line 3: cd: nowhere: No such file or directory