How to prevent bash shell in Windows from exiting? - bash

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.

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.

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 can I source a script using a zsh function, without spawning a subshell, to set environment vars?

I'm trying to run a zsh/bash script that writes several values to environment variables, I want these variables available to the parent shell as they are required for several tools we use. If I manually run the script using '. myscript myparamater' I get the expected result, however defining a zsh function to do this without having to use dot notation does not result in the variables being set.
I'm pretty new to zsh/bash scripting, and this has been my first real effort at writing something useful. I am running this on MacOS but would like for it to work in Linux as well, the script my function is sourcing is doing some bash logic and in some cases also executing a third-party executable (really a bash script that calls a java binary). In my script I'm calling the third-party tool directly using its executable name, calling it using exec or dot notation does not seem to work properly.
zsh Function:
function okta-auth {
. okta_setprofile $1
}
My script:
https://gist.github.com/ckizziar/a60a84a6a148a8fd7b0ef536409352d3
Using '. okta_setprofile myprofile' I receive the expected output of the okta_setprofile script, four environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION, and AWS_SESSION_TOKEN) are set in my shell.
Using 'okta-auth myprofile' the script feedback is the same as previously, however after execution, the environment variables are not set or updated.
Updated 20190206 to show flow
okta_setprofile flow diagam

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