Running a bash script stored in a variable - bash

I want to send the path to a bash script which sources some environmental variables as an argument to another bash script to run it and use the environmental variables. It works well with no arguments if I hard coded the path to the bash script to run it works and I can retrieve the environmental variables in the main script. the problem happens when I send the path as an argument it does not want to run it.
for example if the path is /path/script.bash and I send the path as an argument I get the error that /path/env_set: No such a file or directory
I run the script by this line
. $1 (this doesn't work)
. /path/script.bash (this works)
if I use
bash -c $1
the bash file runs but it does not set the environmental variables to use it in the main script
I don't know why env_set replaces the script name when I use arguments. Is there any approach to achieve this or any work around to achieve my goal?

It sounds like the problem could be either with your quoting, or with relative paths.
Quoting isn't just about spaces, it's also about pathname expansion (ie. []?* characters).
Do
. "$1"
(not . $1)
And remember, if you're giving a relative path for the environment script (or that script uses some relative paths), you will have a problem. Those paths are relative to the pwd - which is wherever you happen to be when you execute the main script (not where any of the script files themselves happen to be located, for example).
Finally, you can debug this problem by throwing echo at the start, and running the script (if it's safe to do that):
echo . "$1"
exit # Add exit here if you don't want to run w/o the vars.
Now you can see what you're actually trying to source.

In script 1, in your main code, you can call and run script 2,
. ./script 2
The first . stands for current shell, and the second . for current directory.
which will create the environment variables for you, and configure any other settings as well in the same terminal.
Afterwards when script 2 has finished running, script 1 would continue to run, and your environment variables which was created in script 2, will be accessible for script 1 to use in the same session.

Related

Setting environment variable to shell executed from perl script

I am running a perl file a.pm which invokes b.sh via system command.
Here, b.sh is using find utility whose path is /usr/local/bin.
If I run env on shell directly on machine, I get output as below for PATH variable.
PATH=/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/home/bin:/home/bin/samba::/home/venv/bin/:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin`
Thats why if I run the b.sh directly from shell, it is able to execute find utility.
Now, If I run b.sh via a.pm as mentioned earlier using system(), and when I print PATH env variable in b.sh, its coming as
/bin:/usr/bin:/usr/X11R6/bin:/home/bin:/home/perl5/bin
which does not have /usr/local/bin, and thats why find command is failing.
If I tried to print all ENV variables in perl before invoking system(b.sh), PATH variable is not printed.
Now, I tried adding path variable in a.pm file as follows just before invoking system(b.sh).
$ENV{'PATH'} = '/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin:/home/bin:/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/';
Now, if I try to print all ENV variables in perl before invoking system(b.sh), PATH variable is printed with above value.
Still executing the a.pm file, the PATH variable printed in b.sh is same:
/bin:/usr/bin:/usr/X11R6/bin:/home/bin:/home/perl5/bin
How can I add corresponding path /usr/local/bin to shell of b.sh invoked using a.pm?
I suspect that the Perl program is either modifying the path it gets from the shell that invokes it, or that you have left out a step somewhere. For example, if you invoke the Perl program from a different environment, it will likely have a different PATH.
You seemed to have found your answer though. Add the necessary directory to the PATH in the Perl program. But, you say this doesn't work. Again, I think there's some step that you haven't included. I suspect that the way in which you run system overwrites the PATH inherited from the parent.
For example, here's a small Perl program the merely runs a shell script:
#!perl
use v5.10;
$ENV{PATH} = '/bin:/usr/bin';
say "PATH in Perl is $ENV{PATH}";
system( "sh ./pather.sh" );
The shell script echos the PATH:
#!/bin/sh
echo "PATH in shell:" $PATH
When I run this, both PATHs match:
PATH in Perl is /bin:/usr/bin
PATH in shell: /bin:/usr/bin
But, maybe the command in system is something else. The -l switch treats the shell as a login shell, so it will load the various profiles and whatnot:
system( "sh -l ./pather.sh" );
Now the PATH is different in the shell script because my particular profiles overwrote PATH:
PATH in Perl is /bin:/usr/bin
Path in shell: /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/TeX/texbin:/usr/local/go/bin:...
Our answers can be more targeted you can produce a minimal working example where we see actual code that demonstrates the problem. Since we don't see what you actually run in system, we can only guess.

Is there a good way to preload or include a script prior to executing another script?

I am looking to execute a script but have it include another script before it executes. The problem is, the included script would be generated and the executed script would be unmodifiable. One solution I came up with, was to actually reverse the include, by having the include script as a wrapper, calling set to set the arguments for the executed script and then dotting/sourcing it. E.g.
#!/bin/bash
# Generated wrapper or include script.
: Performing some setup...
target_script=$1 ; shift
set -- "$#"
. "$target_script"
Where target_script is the script I actually want to run, importing settings from the wrapper.
However, the potential problem I face is that callers of the target script or even the target script itself may be expecting $0 to be set to the path of it's location on the file system. But because this wrapper approach overrides $0, the value of $0 may be unexpected and could produce undefined behaviour.
Is there another way to perform what is in effect, an LD_PRELOAD but in the scripted form, through bash without interfering with its runtime parameters?
I have looked at --init-file or --rcfile, but these only seem to be included for interactive shells.
Forcing interactive mode does seem to allow me to specify --rcfile:
$ bash --rcfile /tmp/x-include.sh -i /tmp/xx.sh
include_script: $0=bash, $BASH_SOURCE=/tmp/x-include.sh
target_script: $0=/tmp/xx.sh, $BASH_SOURCE=/tmp/xx.sh
Content of the x-include.sh script:
#!/bin/bash
echo "include_script: \$0=$0, \$BASH_SOURCE=$BASH_SOURCE"
Content of the xx.sh script:
#!/bin/bash
echo "target_script: \$0=$0, \$BASH_SOURCE=$BASH_SOURCE"
From the bash documentation:
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
but the value of the PATH variable is not used to search for the file name.
So that settles it then:
BASH_ENV=/tmp/x-include.sh /bin/bash /tmp/xx.sh

How can I store and execute the command "export PATH=$PREFIX/bin" from a script?

I would like to write a script that has several commands of the kind
> export PATH=$PREFIX/bin
Where
> $PREFIX = /home/usr
or something else. Instead of typing it into the the Shell (/bin/bash) I would run the script to execute the commands.
Tried it with sh and then with a .py script having the line,
> commands.getstatusoutput('export PATH=$PREFIX/bin')
but these result into the error "bad variable name".
Would be thankful for some ideas!
If you need to adjust PATH (or any other environment variable) via a script after your .profile and equivalents have been run, you need to 'dot' or 'source' the file containing the script:
. file_setting_path
source file_setting_path
The . notation applies to all Bourne shell derivatives, and is standardized by POSIX. The source notation is used in C shell and has infected Bash completely unnecessarily.
Note that the file (file_setting_path) can be specified as a pathname, or if it lives in a directory listed on $PATH, it will be found. It only needs to be readable; it does not have to be executable.
The way the dot command works is that it reads the named file as part of the current shell environment, rather than executing it in a sub-shell like a normal script would be executed. Normally, the sub-shell sets its environment happily, but that doesn't affect the calling script.
The bad variable name is probably just a complaint that $PREFIX is undefined.
Usually a setting of PATH would look something like
export PATH=$PATH:/new/path/to/programs
so that you retain the old PATH but add something onto the end.
You are best off putting such things in your .bashrc so that they get run every time you log in.

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.

How do I set bash environment variables from a script?

I have some proxy settings that I only occasionally want to turn on, so I don't want to put them in my ~/.bash_profile. I tried putting them directly in ~/bin/set_proxy_env.sh, adding ~/bin to my PATH, and chmod +xing the script but though the script runs, the variables don't stick in my shell. Does anyone know how to get them to stick around for the rest of the shell session?
Use one of:
source <file>
. <file>
In the script use
export varname=value
and also execute the script with:
source set_proxy_env.sh.
The export keyword ensures the variable is marked for automatic inclusion in the environment of subsequently executed commands. Using source to execute a script starts it with the present shell instead of launching a temporary one for the script.
Did you try this:
. ~/bin/set_proxy_env.sh
Running it by itself opens a separate subshell (I think) and sets the variable there. But then the binding is lost after exiting back into your shell. The dot at the front tells it to run it within the same shell.
Also, don't forget to export the variables you need like so: export MYVAR=value

Resources