Bash: exported variable not loaded in sh script - bash

I have the following test.sh script:
#!/bin/sh
echo "MY_VARIABLE=$MY_VARIABLE"
Well, if I execute the following:
export MY_VARIABLE=SOMEVALUE
/bin/bash test.sh
it prints:
MY_VARIABLE=
Why the MY_VARIABLE is not read in the test.sh script?
You can reproduce the context here using the following script:
touch test.sh
chmod a+x test.sh
echo "#!/bin/sh" >> test.sh
echo "echo "MY_VARIABLE=$MY_VARIABLE"" >> test.sh
export MY_VARIABLE=something
/bin/bash test.sh

In your script to create the context, the line
echo "echo "MY_VARIABLE=$MY_VARIABLE"" >> test.sh
creates the following line in test.sh:
echo MY_VARIABLE=
if MY_VARIABLE was unset before. The expansion of $MY_VARIABLE is done in the shell that prepares your context.
If you use single quotes
echo 'echo "MY_VARIABLE=$MY_VARIABLE"' >> test.sh
the script test.sh contains the correct line
echo "MY_VARIABLE=$MY_VARIABLE"
and prints MY_VARIABLE=something as expected.

Everything works well but if you want your parent process to keep environment update, you must source your script:
source test.sh
Otherwise, changes will only have effect during the execution of your script.
You can consider it the same as sourcing your ~/.bashrc file.

Related

Argument evaluation

Look at this bash script (script.sh):
#!/bin/bash
echo "aaa"
echo "bbb"
...
echo $1
...
Now, i am trying to run this script this way:
./script.sh $(cat file1)
I have a problem:
the "cat file1" is run before script.sh. Bash is evaluating all arguments before running script.sh
I would like to run "cat file1" inside script.sh, on the "echo $1" line.
How can i do this ?
I have tried this:
./script.sh $(eval 'cat file1')
But it gives me the same result...
Thanks
I think, there is no way unless you can modify the script script.sh.
If you can modify script.sh, you can write your script like this:
#!/bin/bash
echo "$($1)"
Then call it in this way:
./script.sh "cat file1"
This means, you pass the command to be executed to the script and you echo the result of the executed command in the script.
But the above script is a little cumbersome. This one is simpler and would do the same:
#!/bin/bash
$1

Can I put a breakpoint in shell script?

Is there a way to suspend the execution of the shell script to inspect the state of the environment or execute random commands?
alias combined with eval gives you basic functionality of breakpoints in calling context:
#!/bin/bash
shopt -s expand_aliases
alias breakpoint='
while read -p"Debugging(Ctrl-d to exit)> " debugging_line
do
eval "$debugging_line"
done'
f(){
local var=1
breakpoint
echo $'\n'"After breakpoint, var=$var"
}
f
At the breakpoint, you can input
echo $var
followed by
var=2
then Ctrl-d to exit from breakpoint.
Due to eval in the while loop, use with caution.
Bash or shell scripts do not have such debugging capabilities as other programming languages like Java, Python, etc.
We can put the echo "VAR_NAME=$VAR_NAME" command in the code where we want to log the variable value.
Also, a little bit more flexible solution is to put this code somewhere at the beginning in the shell script we want to debug:
function BREAKPOINT() {
BREAKPOINT_NAME=$1
echo "Enter breakpoint $BREAKPOINT_NAME"
set +e
/bin/bash
BREAKPOINT_EXIT_CODE=$?
set -e
if [[ $BREAKPOINT_EXIT_CODE -eq 0 ]]; then
echo "Continue after breakpoint $BREAKPOINT_NAME"
else
echo "Terminate after breakpoint $BREAKPOINT_NAME"
exit $BREAKPOINT_EXIT_CODE
fi
}
export -f BREAKPOINT
and then later, at the line of code where we need to break we invoke this function like this:
# some shell script here
BREAKPOINT MyBreakPoint
# and some other shell script here
So then the BREAKPOINT function will log some output then launch /bin/bash where we can run any echo or some other shell command we want. When we want to continue running the rest of the shell script (release breakpoint) we just need to execute exit command. If we need to terminate script execution we would run exit 1 command.
There exist solutions like bash-debug.
A poor-man's solution which works for me is the interactive shell.
By adding three lines of code, you can introspect and alter variables as follows:
Let's assume, that you have the script test.bash
A=FOO
export B=BAR
echo $A
echo $B
$ test.bash
FOO
BAR
If you add an interactive shell at line 3, you can look around and inspect variables which have been exported before:
A=FOO
export B=BAR
bash -c "$SHELL"
echo $A
echo $B
$ test.bash
$ echo $A
$ echo $B
BAR
$ exit
FOO
BAR
If you want to see all variables in your interactive shell, you have to add set -a to the preamble of your script, such that all variables and functions are exported:
set -a
A=FOO
export B=BAR
bash -c "$SHELL"
echo $A
echo $B
$ test.bash
$ echo $A
FOO
$ echo $B
BAR
$ exit
FOO
BAR
Note, that you cannot change the variables in your interactive shell. The only solution for me is to source an additional script of variables, which will be sourced rightafter the interactive shell
set -a
A=FOO
export B=BAR
bash -c "$SHELL"
source /tmp/var
echo $A
echo $B
$ test.bash
$ echo "export A=alice" > /tmp/var
$ echo "export B=bob" >> /tmp/var
$ exit
alice
bob

Saving the result of an echo command in a shell script?

I am attempting to store the result of an echo command as a variable to be used in a shell script. Debian 4.19.0-6-amd64
The command works in terminal: echo $HOSTNAME returns debian-base, the correct hostname.
I attempt to run it in a shell script, such as:
#!/usr/bin/bash
CURRENT_HOSTNAME=`echo $HOSTNAME`
echo $CURRENT_HOSTNAME
I have tried expansion:
CURRENT_HOSTNAME=$(echo $HOSTNAME)
And just to cover some more bases, I tried things like:
CURRENT_HOSTNAME=$HOSTNAME
# or
CURRENT_HOSTNAME="$HOSTNAME"
# also, in case a problem with reserved names:
test=$HOSTNAME
test="$HOSTNAME"
Works great in the terminal! Output is as follows:
root#debian-base:/scripts# echo $HOSTNAME
debian-base
root#debian-base:/scripts# TEST_HOSTNAME=$HOSTNAME
root#debian-base:/scripts# echo $TEST_HOSTNAME
debian-base
root#debian-base:/scripts# TEST_TWO_HOSTNAME=$(echo $HOSTNAME)
root#debian-base:/scripts# echo $TEST_TWO_HOSTNAME
debian-base
As soon as I run the script (as above):
root#debian-base:/scripts# sh test.sh
root#debian-base:/scripts#
What am I doing wrong?
You are using bash as your terminal. Bash has the variable $HOSTNAME set. You run your script with sh. sh does not have a $HOSTNAME.
Options:
bash test.sh
Or run it as a program:
chmod +x test.sh
./test.sh
But I think you need to change your first line to:
#!/bin/bash
As I don't think bash is installed in /usr/bin in most cases. But you need to try. To figure out where bash is installed use which bash
Another option is to use the hostname binary:
CURRENT_HOSTNAME=$(hostname)
echo $CURRENT_HOSTNAME
Which works in both bash and sh.
You can start sh by just running sh. You will see it has a bash-like terminal. You can try to do echo $HOSTNAME. It will not show, because it's not there. You can use set to see all the variables that are there (as sh does not have tab completion it's harder to figure out).

How to pass argument in bash pipe from terminal

i have a bash script show below in a file called test.sh
#!/usr/bin/env bash
echo $1
echo "execution done"
when i execute this script using
Case-1
./test.sh "started"
started
execution done
showing properly
Case-2
If i execute with
bash test.sh "started"
i'm getting the out put as
started
execution done
But i would like to execute this using a cat or wget command with arguments
For example like.
Q1
cat test.sh |bash
Or using a command
Q2
wget -qO - "url contain bash" |bash
So in Q1 and Q2 how do i pass argument
Something simlar to this shown in this github
https://github.com/creationix/nvm
Please refer installation script
$ bash <(curl -Ls url_contains_bash_script) arg1 arg2
Explanation:
$ echo -e 'echo "$1"\necho "done"' >test.sh
$ cat test.sh
echo "$1"
echo "done"
$ bash <(cat test.sh) "hello"
hello
done
$ bash <(echo -e 'echo "$1"\necho "done"') "hello"
hello
done
You don't need to pipe to bash; bash runs as standard in your terminal.
If I have a script and I have to use cat, this is what I'll do:
cat script.sh > file.sh; chmod 755 file.sh; ./file.sh arg1 arg2 arg3
script.sh is the source script. You can replace that call with anything you want.
This has security implications though; just running an arbitrary code in your shell - especially with wget where the code comes from a remote location.

export /etc/environment variables to current environment; from a shell script?

How do I bring environment variables from /etc/environment to the terminal and what it calls?
file0.bash
#!/usr/bin/env bash
bash ./file1.bash
echo $FOO_BAR
for line in $( sudo cat /etc/environment ); do export $line; done
file1.bash
#!/usr/bin/env bash
sudo sed -i '/^FOO_BAR/d' /etc/environment
printf FOO_BAR="$HOME/Foo\n" | sudo tee -a /etc/environment
for line in $( sudo cat /etc/environment ); do export $line; done
Console
$ echo $FOO_BAR
$ bash file0.bash
[sudo] password for myusername:
FOO_BAR=/home/myusername/Foo
$ echo $FOO_BAR
$ # What I want to avoid is having to revert to this:
$ for line in $( sudo cat /etc/environment ); do export $line; done
$ echo $FOO_BAR
/home/myusername/Foo
When you execute a script as:
bash ./file.bash
OR else:
./file1.bash
Running a shell script like this launches a new process, a subshell.
All the variables created in a subshell are not visible outside the block of code in the subshell. They are not accessible to the parent process, to the shell that launched the subshell. These are, in effect, variables local to the child process. Note that exporting variables also won't make them available in the parent shell. That just makes them available to further subshells of the running subshell.
To change this behavior you can force script to execute in current shell itself using any of these 2 way:
source ./file1.bash
OR
. ./file1.bash

Resources