I have a lot of bash scripts that work on an exported variable called WORKING_FILE.
This variable is exported in the main.sh script which is the first script launched in a shell and during his execution it calls other scripts (e. g. child.sh).
Actually, child.sh see the WORKING_FILE as a copy of the main.sh environment and if it tries to change the WORKING_FILE the value will be lost at the end of its execution.
So, the question is:
how can child.sh modify the value of WORKING_FILE and change the WORKING_FILE value also in the main.sh without using files?
Thanks. :)
If you export the variable and execute the child, then you cannot -- the child gets a copy of the variable, and any changes are not visible in the parent.
You can, however, source the script,
. ./child.sh
or
source child.sh
This will execute the child script in the same interpreter and any changes to the environment this script makes will be visible in your script.
Related
I use variables in my script and run the script in the following way . ./test.sh. And as it should be, after the script finishes, all the set variables remain in the shell; I know that at the end of the script one can add unset variable variable2. But what if there are a lot of variables, is there a way/command to unset all set variables in the script at once at the end?
Do you need to run it with .? Normally one does that in order to have variable assignments persist afterward.
If you run the script with just ./test.sh then it'll run as a child process with a separate environment from the parent shell. Variable assignments in the child will not affect the parent, and they'll be lost when the child exits.
I'm trying to make variables exits outside of a shell script without using source.
The variables are declared in shell script with
export varA=3
and I run the script with ./filename.sh
I want
echo $varA
in the terminal to return 3 (i.e. the value of varA). So extend the scope of the variable to outside of the script
To sum up: how do I make the variables inside a shell script exist outside.
Thank you in advance
You can run your script on this way:
. ./filename.sh
This mean when run it will not spawn new shell but run it in current. And variables you set in your script will be available in your shell. This is kind of "source" as mentioned in comments.
I have a set of environment variables that need to be set on the basis of the arguments specified in the shell script.
But the problem is that those variables are already defined in the bash profile
FOR EXAMPLE:
bash_profile has a variable called "KARAN":
export KARAN=/config/1
Now on running the shell script, this is what it should do:
export KARAN=/config/2 (Changed the bash profile's KARAN value to 2)
Your question is not clear. If your script needs to set the env var to a specific value just do so using export VAR=val. What I think you're asking is how to have a script modify the environment of the current shell. And that is impossible without the cooperation of both shells. That is because environment vars are inherited by child processes. But a child process cannot directly modify the environment of its parent process (or some other random process for that matter). To do so the two processes must coordinate the exchange of data. This is typically done by using the source command if the child process is a shell script. Or by having the child process write a series of export statements to stdout and having the parent shell capture and execute those statements. For example, let's say I have a script named set_env that looks like this
#!/bin/sh
echo export KARAN=/config_2
echo export VAR2=val2
The current shell would then do
eval $(set_env)
Note, however, eval is dangerous. I prefer to do this which is slightly safer:
set_env | source /dev/stdin
That, however, only works in shells like ksh and zsh. Due to how bash handles pipelines the source is actually executed in a child shell and therefore the vars won't be set in the current shell.
You can create a new Profile with all the new definitions. and then call the line below on top of your shell script. Similarly, you can create as many profiles as you want and use it.
source bash_profile_new
Why does this work:
# a.sh
setEnv() {
export TEST_A='Set'
}
when this doesn't:
# b.sh
export TEST_B='Set'
Ex:
> source a.sh
> setEnv
> env | grep TEST_A
TEST_A=Set
> b.sh
> env | grep TEST_B
I understand why running the script doesn't work and what to do to make it work (source b.sh etc), but I'm curious to why the function works.
This is on OS X if that matters.
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 are retained until the parent-shell is terminated (the terminal is closed, or the variables are reset or unset), whereas
Execute forks a new shell from the parent shell and those variables including your export variables are retained only in the sub-shell's environment and destroyed at the end of script termination.
i.e. the sub-shell ( imagine it being an environment) created in the first case to hold the variables are not allocated in scope of a separate child environment but are just added in the parents' ( e.g. imagine an extra memory cell, maintained by the parent ) environment which is held until you have the session open. But executing a script is, imagine a simple analogy, calling a function whose variables are in stored in stack which loose scope at the end of function call. Likewise, the forked shell's environment looses scope at the end of its termination.
So it comes down to this, even if you have a function to export your variable, if you don't source it to the current shell and just plainly execute it, the variable is not retained; i.e.
# a.sh
setEnv() {
export TEST_A='Set'
}
and if you run it in the shell as
bash script.sh # unlike/NOT source script.sh
env | grep TEST_A
# empty
Executing a function does not, in and of itself, start a new process like b.sh does.
From the man page (emphasis on the last sentence):
FUNCTIONS
A shell function, defined as described above under SHELL GRAMMAR,
stores a series of commands for later execution. When the name of a
shell function is used as a simple command name, the list of commands
associated with that function name is executed. **Functions are executed
in the context of the current shell; no new process is created to
interpret them (contrast this with the execution of a shell script).**
I understand why running the script doesn't work and what to do to make it work (source b.sh etc)
So you already understand the fact that executing b.sh directly -- in a child process, whose changes to the environment fundamentally won't be visible to the current process (shell) -- will not define TEST_B in the current (shell) process, so we can take this scenario out of the picture.
I'm curious why the function works.
When you source a script, you execute it in the context of the current shell - loosely speaking, it is as if you had typed the contents of the script directly at the prompt: any changes to the environment, including shell-specific elements such as shell variables, aliases, functions, become visible to the current shell.
Therefore, after executing source a.sh, function setEnv is now available in the current shell, and invoking it executes export TEST_A='Set', which defines environment variable TEST_A in the current shell (and subsequently created child processes would see it).
Perhaps your misconception is around what chepner's helpful answer addresses: in POSIX-like shells, functions run in the current shell - in contrast with scripts (when run without source), for which a child process is created.
This is on OS X if that matters.
Not in this case, because only functionality built into bash itself is used.
I have a file run_me
#!/bin/bash
export BOBO=MOMO
After I run run_me, the variable BOBO isn't set. Why? How can it be fixed?
You need to source it:
. ./run_me
OR
source ./run_me
In order to run this script in current shell otherwise BASH creates a new sub-shell and executes the script in that sub-shell therefore all the changes (variable etc) are not reflected in the current parent shell.