run python script in current shell - bash

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.

Related

Do bash scripts execute in new shells or subshells?

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.

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.

How to override sha bang interpreter in shell scripts recursively?

I've a master shell script which calls functions defined in various other shell scripts. The master script includes other scripts using 'source' command.
I want to use a common interpreter for all the scripts regardless of what the she bang ("#!/bin/sh") has been set to in those scripts. I want to supply that interpreter from command line.
for example:
master.sh (with #!/bin/sh)
subscript1.sh (with #!/bin/sh)
subscript2.sh (with #!/bin/sh)
subscript3.sh (with #!/bin/sh)
master.sh calls functions which are defined in the subscripts and are included as 'source subscript1.sh', 'source subscript2.sh' and 'source subscript3.sh'.
When I run ./master.sh, the subscript use their respective interpreters as directed by "#!/bin/sh" line. I want to run all of them using '/bin/bash', the master and the subscripts but without changing the she bang line because there are a lot of such scripts. Is there any way to do this?
Call the interpreter explicitly:
bash ./master.sh
Note that the shebang line has no effect on scripts run using source. That command always executes the script in the current shell process, so it uses whatever interpreter is currently running.
But this all seems dangerous. If someone writes #!/bin/sh instead of #!/bin/bash, it may have dependencies on sh syntax that would be violated if bash were used instead.

Bash interactive and non-interactive shell behaviour

I have a hard time with interactive and non-interactive shells. I don't understand which is which.
For example, I have read that non interactive shells usually check for the BASH_ENV variable on their startup and execute whatever it points to.
So, what I did is I set the BASH_ENV to point to some script which only echoes OK. Then I typed in bash in terminal and this script echoed OK. But why? Didn't I call yet another INTERACTIVE shell by typing bash in terminal, and not the other way around? Why did it execute the bash_env? I'm on linux mint maya.
The only thing you can be certain of is what's shown in the manpage for bash (see INVOCATION) - that lists in details what startup files are run in each instance.
However, there's nothing stopping (for example) one of those startup files running other files which would normally not be run.
By way of example, if .bash_profile had the following line:
. ~/.profile
it would also run the .profile script.
In fact the manpage states:
When bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV in the environment, expands its value if it appears there, and uses the expanded value as the name of a file to read and execute. Bash behaves as if the following command were executed:
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
So, if you put that exact line in your startup scripts for an interactive shell like ~/.bash_profile, you'll also source the file pointed to by BASH_ENV.
Your best bet is to examine the INVOCATION section to find out which of the files will run, and then track through them (with something like set -x at the top of the script) to see what's getting called from where.
If memory serves, Bash is only interactive if you tell it, example
bash -i
So, by you calling just bash you invoked a non-interactive Bash.
More info
-i
If the -i option is present, the shell is interactive.

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