Difference between different ways of running shell script - linux

Recently I have been asked a question. What are the different ways of executing shell script and what is the difference between each methods ?
I said we can run shell script in the following methods assuming test.sh is the script name,
sh test.sh
./test.sh
. ./test.sh
I don't know the difference between 1 & 2. But usually in first 2 methods, upon executing, it will spawn new process and run the same. Whereas in the last method, it won't spawn new process. Instead it runs in the same one.
Can someone throw more insight on this and correct me if I am wrong?

sh test.sh
Tells the command to use sh to execute test.sh.
./test.sh
Tells the command to execute the script. The interpreter needs to be defined in the first line with something like #!/bin/sh or #!/bin/bash. Note (thanks keltar) that in this case the file test.sh needs to have execution rights for the user performing this command. Otherwise it will not be executed.
In both cases, all variables used will expire after the script is executed.
. ./test.sh
Sources the code. That is, it executes it and whatever executed, variables defined, etc, will persist in the session.
For further information, you can check What is the difference between executing a bash script and sourcing a bash script? very good answer:
The differences are:
When you execute the script you are opening a new shell, type
the commands in the new shell, copy the output back to your current
shell, then close the new shell. Any changes to environment will take
effect only in the new shell and will be lost once the new shell is
closed.
When you source the script you are typing the commands in your
current shell. Any changes to the environment will take effect and stay in your current shell.

Related

About script execution permissions on Linux shell

I've just created a script, let's say, "helloworld.sh".
The script doesn't yet have execution permissons: -rw-rw-r--
If I try to execute that script with: "./helloword.sh", I'll get an error message, as expected. But, if I try to execute that same script as: . helloword, it will execute with no problems.
How? Why does that happen?
This happens because on Linux the "." (dot) alone is a built-in command that execute the script within your current session with your current shell. This is the same as calling the script with source command (the BSD default method). It's almost the same than execute with bash helloworld.sh.
When you call the script with ./helloworld.sh or /root/helloworld.sh the shell will try to figure out how to execute it, if the file is a binary, it will simply run, if it is a script, the shell will read the first line looking for the interpreter. To do this, you'll need execution permission.
To simplify:
One is a command;
The other one is a path.
You can even run:
. --help
About . against bash:
This is why we use . or source to load variables from a file in our session, for example, when we change ~/.bashrc and reload it without login again.
You can see this happens when you execute:
. /etc/os-release
All variables defined inside this file will be loaded and available in your current shell session.
The same will not happen if you execute:
bash /etc/os-release
Because you opened a "new session" inside that bash that you called, the new bash executes and close, cleaning the session.
The same process happen if you give execute permission +x to the script, because when you call the script with ./ or something like that, a new session will be created too.

Changing bash prompt temporarily by own script [duplicate]

This question already has answers here:
Changing PS1 prompt in a Bash parent shell
(3 answers)
Closed 6 years ago.
I wanted to write a tiny shell script that shortens the commmand prompt when it gets too long. Setting the PS1 variable in bash works fine. When I try the same command directly in a script and run it, nothing happens.
#!/bin/bash
PS1='\u:\W\$ '
I tried eval "PS1='\u:\W\$ '" , export PS1='\u:\W\$ ' and exec PS1='\u:\W\$ ' without any result.
How can I achieve the same result as a direct input in the bash?
Thank you in advance
In general, in UNIX, a process can only change variables for itself and its children -- not its parent ("its parent" being the process that invoked it).
You need to source a script, not execute it, for it to be able to effect your interactive shell's variables. This executes all commands inside the script inside your current shell, not a new shell started as a child process (whose variables' values are thrown away on exit).
# in bash
source yourscript
# or in POSIX sh
. yourscript # mind the space!
In this usage, the shebang does nothing; similarly, the +x permission isn't needed either. It's also typical to name scripts intended to be sourced rather than executed with an extension mapping to the shell they're intended to be used by (yourscript.bash for bash, yourscript.sh for a script which can be sourced by any POSIX shell), whereas scripts intended to be executed rather than sourced should have no extension.

Why calling a script by "scriptName" doesn't work?

I have a simple script cmakeclean to clean cmake temp files:
#!/bin/bash -f
rm CMakeCache.txt
rm *.cmake
which I call like
$ cmakeclean
And it does remove CMakeCache.txt, but it doesn't remove cmake_install.cmake:
rm: *.cmake: No such file or directory
When I run it like:
$ . cmakeclean
it does remove both.
What is the difference and can I make this script work like an usual linux command (without . in front)?
P.S.
I am sure the both times is same script is executed. To check this I added echo meme in the script and rerun it in both ways.
Remove the -f from your #!/bin/bash -f line.
-f prevents pathname expansion, which means that *.cmake will not match anything. When you run your script as a script, it interprets the shebang line, and in effect runs /bin/bash -f scriptname. When you run it as . scriptname, the shebang is just seen as a comment line and ignored, so the fact that you do not have -f set in your current environment allows it to work as expected.
. script is short for source script which means the current shell executes the commands in the script. If there's an exit in there, the current shell will exit (and e. g. the terminal window will close).
This is typically used to modify the environment of the current shell (set variables etc.).
script asks the shell to fork itself, then exec the given script in the child process, and then wait in the father for the termination of the child. If there's an exit in the script, this will be executed by the child shell and thus only terminate this. The father shell stays intact and unaltered by this call.
This is typically used to start other programs from the current shell.
Is this about ClearCase? What did you do in your poor life where you've been assigned to work in the deepest bowels of hell?
For years, I was a senior ClearCase Administer. I haven't touched it in over a decade. My life is way better now. The sky is bluer, bird songs are more melodious, and my dread over coming to work every day is now a bit less.
Getting back to your issue: It's hard to say exactly what's going on. ClearCase does some wacky things. In a dynamic view, the ClearCase repository on Unix systems is hidden in the shell's environment. Now you see it, now you don't.
When you run a shell script, it starts up a new environment. If a particular shell variable is not imported, it is invisible that shell script. When you merely run cmakeclean from the command line, you are spawning a new shell -- one that does not contain your ClearCase environment.
When you run a shell script with a dot prefix like . cmakeclean, you are running that shell script in the current shell which contains your ClearCase environment. Thus, it can see your ClearCase view.
If you're using a snapshot view, it is possible that you have a $HOME/.bashrc that's changing directories on you. When a new shell environment runs in BASH (the default shell in MacOS X and Linux), it first runs $HOME/.bashrc. If this sets a particular directory, then you end up in that directory and not in the directory where you ran your shell script. I use to see this when I too was involved in ClearCase hell. People setup their .kshrc script (it was the days before BASH and most people used Kornshell) to setup their views. Unfortunately, this made running any other shell script almost impossible to do.

What is the difference between ./a.sh and . ./a.sh? [duplicate]

This question already has answers here:
What is the difference between "./somescript.sh" and ". ./somescript.sh"
(4 answers)
Closed 8 years ago.
What I know is when I am having two script files lets say a.sh and b.sh and to use the variable or function defined in script a.sh, then . ./a.sh works but ./a.sh doesn't works. While running a shell script both ./script.sh and . ./script.sh works fine. What is the difference between running a script with ./script.sh and . ./script.sh?
. path/to/script sources the file (executes it in the same shell). The other call forks a new shell process which executes the script.
Invoking a script in a child process will make its variables not available to the parent process. Sourcing a script will introduce and change variables in the same parent process.
The notation . ./a.sh is short for source ./a.sh. source is a built-in command of the executing shell to read the given file line-by-line and execute all that is written there as if it was typed in the shell directly. As a consequence, if there is an exit statement in a.sh, it will close the shell which sources this; typically, the xterm window closes then.
The notation ./a.sh, however, starts a new process; this is done by the current shell forking itself and then making the forked child executing the given program. This is, in this case, a shell script again, so a new shell will be executed. Everything this new shell does will not influence the original (parent) shell. If the child is not sent to the background, the parent then waits for the child to terminate.

In Linux shell do all commands that work in a shell script also work at the command prompt?

I'm trying to interactively test code before I put it into a script and was wondering if there are any things that behave differently in a script?
When you execute a script it has its own environment variables which are inherited from the parent process (the shell from which you executed the command). Only exported variables will be visible to the child script.
More information:
http://en.wikipedia.org/wiki/Environment_variable
http://www.kingcomputerservices.com/unix_101/understanding_unix_shells_and_environment_variables.htm
By the way, if you want your script to run in the same environment as the shell it is executed in, you can do it with the point command:
. script.sh
This will avoid creating a new process for you shell script.
A script runs in exactly the same way as if you typed the content in at a shell prompt. Even loops and if statements can be typed in at the shell prompt. The shell will keep asking for more until it has a complete statement to execute.
As David rightly pointed out, watch out for environment variables.
Depending on how you intend to launch your script, variables set in .profile and .bashrc may not be available. This is subject to whether the script is launched in interactive mode and whether it was a login shell. See Quick Startup File Reference.
A common problem I see is scripts that work when run from the shell but fail when run from another application (cron, nagios, buildbot, etc.) because $PATH was not set.
To test if a command/script would work in a clean session, you can login using:
ssh -t localhost "/bin/bash --noprofile --norc"
This ensures that we don't inherit any exported variables from the parent shell, and nothing from .profile or .rc.
If it works in a clean session and none of you're commands expect to be in interactive mode, then you're good to go!

Resources