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
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