Do bash scripts execute in new shells or subshells? - bash

I am running a bash script from my bash interactive shell as:
./shell.sh
The confusion I am having is, will this script run inside a new shell instance or a subshell of my current bash instance?
I assume that all shell scripts invoked from a shell run inside a new shell therefore they aren't able to read the local shell variables of the invoking shell.
Also, if I put "echo $BASH_SUBSHELL" in my invoked script it returns me a value of "0" showing that it isn't a subshell. But according to some articles they say that a shell script when executed from a shell invokes a subshell. Please help.

You're correct; when you run a script with ./shell.sh, it runs in a new shell, not a subshell of the current shell.
It does run in a subprocess, which is a shell, so it's a tempting and common mistake to say "subprocess+shell=subshell, so it must be a subshell!" But that's incorrect. The shell running the script won't inherit shell variables from the parent shell process (it'll inherit environment variables, i.e. exported variables, but that's true of any subprocess), it won't inherit shell modes (e.g. set -e) or other shell state, and it won't even necessarily be running the same shell (if you're running bash and the script has a #!/bin/zsh shebang, it'll run in zsh). So it's logically a different shell that just happens to be running as a subprocess of the shell that launched it.

Related

run python script in current shell

When I need to run a bash script that runs cd somedir to affect the current shell I run it with . scriptname. However, if scriptname is a python script even with #!/usr/bin/env python3 in the first line, it doesn't work, it seems it expects the script to be a bash script. How can I make it work with python scripts (or any other language with the appropriate shebang)?
It is not possible.
The only thing that can affect current process environment is the process itself. Because the current shell is bash, the only thing that can be executed that could affect bash environment is something that can be run by bash itself. That "something" are statements interpreted by bash. Because bash doesn't support interpreting and running python statements, it is not possible.
The usual way around this, is to output from your python script properly escaped assignment statements that would assign environment variables. Then the output from your script is evalulated by bash. This is for example how eval "$(docker-machine ...)" works.

How to prevent bash shell in Windows from exiting?

I am calling a bash shell scripts in Windows by double click using association of the the extension. The problem is that the shell exits immediately unless a blocking call is made. This script exits immediately after setting up some environment variables by calling an "activate" sub-script file, but I want it to not exit and be available for interactive use:
source venv/Scripts/activate
I have tried calling bash hoping that the environment would be passed, but the environment was either not passed, or passed and then re-intialized - although the resulting shell was available for interactive use:
source venv/Scripts/activate
/bin/bash
Notably, the script
source venv/Scripts/activate
python app.py
will make the environment set up by "activate" available to the "python app.py" call.

UNIX/solaris shell script shebang in include file

I have a functions.sh script with a bunch of global functions that i want to use in other scripts. this functions script is written in bash (#!/bin/bash)
Those many scripts had been written over the years, so the older ones or with the #!/bin/sh (which is different from #!/bin/bash in solaris).
My question here is, when you call the functions.sh file (with . /path/to/functions.sh) from within a sh (not bash) script, is the shebang line of "functions.sh" interpreted ?
In a nutshell, can you call a bash written function script from another shell-type script (with proper shebang lines in both) ?
Thanks !
As long as you want to use the function you need to source the scripts and not execute it
source /path/to/functions.sh
or as per the POSIX standards, do
. ./path/to/functions.sh
from within the sh script, which is equivalent to including the contents of function.sh in the file at the point where the command is run.
You need to understand the difference between sourcing and executing a script.
Sourcing runs the script from the parent-shell in which the script is
invoked; all the environment variables, functions are retained until the
parent-shell is terminated (the terminal is closed, or the variables
are reset or unset),
Execute forks a new shell from the parent shell and those variables,functions
including your export variables are retained only in the sub-shell's
environment and destroyed at the end of script termination.
When you source a file, the shebang in that file is ignored (it is not on the first line since it is included in the caller script and is seen as comment).
When you include an old script with #!/bin/sh it will be handled as the shell of the caller. Most things written in /bin/sh will work in bash.
When you are running a sh or ksh script and you include (source) a bash file, all bash specific code will give problems.

When does a sub shell inherits its parent shell env?

Under what circumstances is the environment of the shell passed to the sub-shell?
A subshell always gets all variables from the parent shell.
man bash will describe all the circumstances in which a subshell is used, which are mainly:
command &
command | command and
( command )
The so called environment only includes environment variables (export variable), and is passed on to every sub-process. Even when invoking bash -c command, which is not a sub-shell but a completely new bash instance.
In both cases changed values are not passed back to the parent process.

Why are bash script variables not saving?

I have a simple bash script:
#!/bin/bash
JAVA_HOME=/usr
EC2_HOME=~/ec2-api
echo $EC2_HOME
export PATH=$PATH:$EC2_HOME/bin
I run the script like so
$ ./ec2
/Users/user/ec2-api
The script runs and produces the correct output.
However, when I now try to access the EC2_HOME variable, I get nothing out:
$ echo $EC2_HOME
I get a blank string back. What am I doing wrong?
Do either of the following instead:
source ec2
or
. ec2
(note the . notation is just a shortcut for source)
Explanation:
This is because ./ec2 actually spawns a subshell from your current shell to execute the script, and subshells cannot affect the environment of the parent shell from which it spawned.
Thus, EC2_HOME does get set to /Users/user/ec2-api correctly in the subshell (and similarly the PATH environment variable is updated and exported correctly in the subshell as well), but those changes won't propagate back to your parent shell.
Using source runs the script directly in the current shell without spawning a subshell, so the changes made will persist.
(A note on export: export is used to tell new shells spawned from the current shell to use the variables exported from the current shell. So for any variables you would only use in the current shell, they need not be exported.)
A shell script can never modify the environment of their parent.
To fix your problem, you can use the dot (.) command:
$ . ./ec2
and that should work. In cshell, it would be
% source ./ec2
To learn more about shells and scripts, my best resource is by far Unix power tools.

Resources