I have a bash script that opens two files which has following contents:
file1:
#!/bin/bash
a='Sunday'
file2:
#!/bin/bash
b=$a
Here is my code snippet:
#!/bin/bash
. file1
. file2
echo $b
OUTPUT : Sunday
Here is my question:
What is the scope of the variable 'a' when I open file1 in the shell script?
How to create a shell variable with that kind of scope? Like the one below :
#!/bin/bash
a='Sunday'
. file2
echo $b
Is that possible?
Sourcing a script with . executes the commands from that file as if they were written inline. Sourcing introduces no additional scope or environment.
Writing a='Sunday' has the same effect whether you write it directly or you source a script with that line: it creates a global variable visible in the rest of your script. This also explains why file2 can see $a, because b=$a also executes inline.
Related
I have two files vars.sh and main.sh with the contents:
$ cat vars.sh
#!/bin/bash
fname="$0" # should $0 equal 'vars.sh'?
$ cat main.sh
#!/bin/bash
echo $0
. vars.sh
echo $fname
When I run main.sh I get:
$ ./main.sh
./main.sh
./main.sh
My question is why is $0 inside vars.sh returning main.sh? I read man bash section about $0 but that did not help much.
Sourcing another script involves executing the sourced commands in the current shell. In the current shell, $0 refers to main.sh. You can think of sourcing as similar to "inclusion" or "copy-paste".
However, there does exist a way to get the sourced file name in bash. You can use BASH_SOURCE variable.
If you change vars.sh to:
#!/bin/bash
fname=${BASH_SOURCE[0]}
Then you'll get the sourced file's name as expected.
It is because . (source) includes commands from sourced file, in your case from vars.sh
https://ss64.com/bash/source.html
When a process is started via exec, the first first argument is usually the path to the executable (or whatever the caller decided to pass there as argument). In bash, this argument can be retrieved via $0. In your case, your process is the bash process running main.sh, so that's what is stored there. vars.sh is executed within the same process; hence, $0 is the same.
I have a shell script that sets a variable. I can access it inside the script, but I can't outside of it. Is it possible to make the variable global?
Accessing the variable before it's created returns nothing, as expected:
$ echo $mac
$
Creating the script to create the variable:
#!/bin/bash
mac=$(cat \/sys\/class\/net\/eth0\/address)
echo $mac
exit 0
Running the script gives the current mac address, as expected:
$ ./mac.sh
12:34:56:ab:cd:ef
$
Accessing the variable after its created returns nothing, NOT expected:
$ echo $mac
$
Is there a way I can access this variable at the command line and in other scripts?
A child process can't affect the parent process like that.
You have to use the . (dot) command — or, if you like C shell notations, the source command — to read the script (hence . script or source script):
. ./mac.sh
source ./mac.sh
Or you generate the assignment on standard output and use eval $(script) to set the variable:
$ cat mac.sh
#!/bin/bash
echo mac=$(cat /sys/class/net/eth0/address)
$ bash mac.sh
mac=12:34:56:ab:cd:ef
$ eval $(bash mac.sh)
$ echo $mac
12:34:56:ab:cd:ef
$
Note that if you use no slashes in specifying the script for the dot or source command, then the shell searches for the script in the directories listed in $PATH. The script does not have to be executable; readable is sufficient (and being read-only is beneficial in that you can't run the script accidentally).
It's not clear what all the backslashes in the pathname were supposed to do other than confuse; they're unnecessary.
See ssh-agent for precedent in generating a script like that.
My current script goes like:
#!/bin/bash
victims=*asci*
for f in $victims ; do
awk /some blah blah here/ ;done
so basically takes all files containing ascii in their name and performs an action on them.
I wanted, however, the filenames be entered as a parameter. Like:
bash myscript.sh *log* for example.
When using
#!/bin/bash
victims="$1"
for f in $victims ; do
awk /some blah blah here/ ;done
it doesnt do what expected. Performs only on the first file (as far as I remember).
May I ask for a help? Want the script to perform a function over a bunch of files that contain the parameter in their filename. Im not very experienced in bash, honestly. Thanks, cheers!
If you're just calling awk then you don't even need the for loop. Just pass it all of the file names at once.
awk '/whatever/' "$#"
If you do want to loop over all the command-line arguments, write:
for f in "$#"; do
...
done
Or, since in "$#" is implied:
for f; do
...
done
If you want to store them in an intermediate variable, you need to use an array:
victims=("$#")
for f in "${victims[#]}"; do
...
done
Also, you should avoid explicitly invoking bash. Run the script directly so it can use whatever shell's listed in its shebang line.
bash myscript.sh *log*
./myscript.sh *log*
You need to watch out how you call your script. Suppose your script myscript.sh is simply
victims="$1"
echo "$victims"
and your cwd contains files a.log, another.log and logmore.txt.
Then, executing
myscript.sh *log*
Wil result in simply
a.log
because "*log*" is interpreted by the shell before calling myscript.sh. In fact, you're executing
myscript.sh a.log another.log logmore.txt
and your script only handles the first parameter. Also very funny is, when your cwd contains no file with "log" in its name, your script will result in:
*log*
So, your call should be:
myscript.sh "*log*"
and your script should handle the fact that its input may be a regulare expression iso. an existing filename.
Let's take a little example:
$ cat source.sh
#!/bin/bash
echo "I'm file source-1"
. source-2.sh
And:
$ cat source-2.sh
#!/bin/bash
echo "I'm file source-2"
Now run:
$ ./source.sh
I'm file source-1
I'm file source-2
If I'll change the call of the second file in first:
$ cat source.sh
#!/bin/bash
echo "I'm file source-1"
source source-2.sh
It will have the same effect as using dot.
What is difference between these methods?
The only difference is in portability.
. is the POSIX-standard command for executing commands from a file; source is a more-readable synonym provided by Bash and some other shells. Bash itself, however, makes no distinction between the two.
There is no difference.
From the manual:
source
source filename
A synonym for . (see Bourne Shell Builtins).
This question already has answers here:
How do I get the directory where a Bash script is located from within the script itself?
(74 answers)
Closed 9 years ago.
So I have one bash script which calls another bash script.
The second script is in a different folder.
script1.sh:
"some_other_folder/script2.sh"
# do something
script2.sh:
src=$(pwd) # THIS returns current directory of script1.sh...
# do something
In this second script it has the line src=$(pwd) and since I'm calling that script from another script in a different directory, the $(pwd) returns the current directory of the first script.
Is there any way to get the current directory of the second script using a simple command within that script without having to pass a parameter?
Thanks.
I believe you are looking for ${BASH_SOURCE[0]}, readlinkand dirname (though you can use bash string substitution to avoid dirname)
[jaypal:~/Temp] cat b.sh
#!/bin/bash
./tp/a.sh
[jaypal:~/Temp] pwd
/Volumes/Data/jaypalsingh/Temp
[jaypal:~/Temp] cat tp/a.sh
#!/bin/bash
src=$(pwd)
src2=$( dirname $( readlink -f ${BASH_SOURCE[0]} ) )
echo "$src"
echo "$src2"
[jaypal:~/Temp] ./b.sh
/Volumes/Data/jaypalsingh/Temp
/Volumes/Data/jaypalsingh/Temp/tp/
Please try this to see if it helps
loc=`dirname $BASH_SOURCE`