4

I have this at the very top of my .bashrc, before the return for non-interactive shells

FOO="BAR"; export FOO
echo "HELLO WORLD"

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

I have a script test.sh in my homedirectory with this:

#!/bin/bash
echo "A"
echo $FOO
echo "B"

I execute test.sh. The output:

A

B

2 Questions:

  • Why don't I see the value of $FOO?
  • Why don't I see the "HELLO WORLD"?

edit: I thought the script with #!/bin/bash triggers a subshell which will call the .bashrc again, am I that wrong?

edit: Even If I do call the script from another host I won't see any values. Not even then , the .bashrc will be executed???

ssh remotehost "/home/username/test.sh"
Preexo
  • 2,102
  • 5
  • 29
  • 37

4 Answers4

4

.bashrc is only sourced automatically for non-login interactive shells. Often, you would put . .bashrc near the beginning of your .bash_login file, to ensure that .bashrc is sourced for both login and non-login interactive shells.

.bashrc is not sourced automatically for non-interactive shells, such as those started when you execute a shell script.

Since you export FOO from .bashrc, the fact that test.sh sees FOO having a null value tells me that you are running the script from a login shell. Does echo $FOO from the prompt print BAR? I would be surprised if it did and test.sh did not.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • if I login again I see `HELLO WORLD` and if I do `echo $FOO` from the prompt I see `BAR`. If I execute `test.sh` after a new login it prints `BAR` as well. So that means the `.bashrc` won't be executed if I run a shellscript from a loginshell. That also means the shellscript doesn't run in a subshell? If it's that simple, I have been wrong a long time and will accept this as an answer. – Preexo Jun 21 '13 at 08:18
  • The script *is* run in a subshell; it simply doesn't source `.bashrc` prior to executing the script. In `bash`, you can specify a file to source when a non-interactive shell starts using the `BASH_ENV` variable. – chepner Jun 21 '13 at 13:24
3

When you logged in again, the bashrc was run in your new login shell, and the shell script inherited the variable from the login shell. That's what "environment variable" means: it's a variable that is passed to child processes.

Scripts do run in a separate process, but I wouldn't use the word "subshell" for those. I reserve that for things like ( cmd1 ; cmd2 ) which creates a subshell to run the stuff in parentheses. Running a shell script causes a fresh exec of the interpreter, which is an important difference as seen here:

x=foo # not exported
export y=bar

(
  # This subshell is a copy of the existing shell process, including the
  # non-exported variables, so x is still foo here.
  echo x is $x
  echo y is $y
  # But it's a copy, so modifications do not propagate to the parent.
  x=blah
  y=blah
) | sed 's/^/SUBSHELL: /'

# Back in the parent shell, x and y have not become blah.
echo PARENT: x is $x
echo PARENT: y is $y

# This is not a "subshell" but a new shell. It inherits the exported
# variable y, but not x. Running a shell script resembles this.
sh -c 'echo x is $x ; echo y is $y' | \
sed 's/^/NEWSHELL: /'
  • very descriptive example, thanks. It's getting clearer now, just one tiny uncertainty left: if running a shell script means that a fresh exec of the interpreter is run, why won't the .bashrc be executed? – Preexo Jun 21 '13 at 09:38
  • Because shell scripts run the shell in non-interactive mode. –  Jun 21 '13 at 09:45
  • fair enough... I thought .bashrc would still be called but returns because of `[ -z "$PS1" ] && return` at the top. Which actions would run a shell in non-interactive mode but still trigger the .bashrc? Sorry for asking so many small and dumb questions but I feel like I have to understand this now. – Preexo Jun 21 '13 at 11:26
  • I have no idea why that `[ -z "$PS1" ] && return` is in there. As far as I can tell it's not necessary. You could source tbe .bashrc from a script, and the PS1 test would cause it to do nothing. Which can't be the explanation... if you deliberately add `. ~/.bashrc` to a script, you probably want it to do something. Maybe it's there to allow the file to be sourced from another file which may either be sourced in an interactive shell or run as a script, or some other complicated scenario. Or maybe someone was just confused when they wrote it. –  Jun 21 '13 at 18:52
1

The answer is simple. Solution in the right order:

Why don't I see the "HELLO WORLD"?

Note that the .bashrc will not being parsed on every command or when starting a sub shell. It will only get parsed on a new shell. From the man page:

When an interactive shell that is not a login shell is started, bash reads and executes commands from /etc/bash.bashrc and ~/.bashrc, if these files exist.

So, I guess your .bashrc won't get processed. Have you tried source ~/.bashrc before executing your script? Also you can just open a new terminal.

Why don't I see the value of $FOO?

If bashrc will get parsed it would work as expected.


General advice: If you are using a variable in a shell command, like echo, use parentheses around the variable's name to make sure the content of the variable will not being treated as an shell option. So

echo $FOO

should be

echo "$FOO"
hek2mgl
  • 152,036
  • 28
  • 249
  • 266
  • Thanks for your advise... OK, thanks for your help. Means a shellscript with `#!/bin/bash` at top won't trigger the `.bashrc`. That's why I don't see any output. Does it still trigger a subshell though? – Preexo Jun 21 '13 at 08:22
1

From my man bash:

If the program is a file beginning with #!, the remainder of the first line specifies an interpreter for the program. The shell executes the specified interpreter on operating systems that do not handle this executable format themselves. The arguments to the interpreter consist of a single optional argument following the interpreter name on the first line of the program, followed by the name of the program, followed by the command arguments, if any.

So I guess a good lawyer could argue that bash can interpret bash and therefore does not execute another interpreter.

rectummelancolique
  • 2,247
  • 17
  • 13