What occurs when a file is `source`-d in Unix/Linux context? - linux

I've seen shell scripts that include a line such as:
source someOtherFile
I know that causes the content of someOtherFile to execute, but what is the significance of source?
Follow-up questions: Can ANY script be sourced, or only certain type of scripts? Are there any side-effects other than environment variables when a script is sourced (as opposed to normally executing it)?

Running the command source on a script executes the script within the context of the current process. This means that environment variables set by the script remain available after it's finished running. This is in contrast to running a script normally, in which case environment variables set within the newly-spawned process will be lost once the script exits.
You can source any runnable shell script. The end effect will be the same as if you had typed the commands in the script into your terminal. For example, if the script changes directories, when it finishes running, your current working directory will have changed.

If you tell the shell, e.g. bash, to read a file and execute the commands in the file, it's called sourcing. The main point is, the current process (shell) does this, not a new child process.
In BASH you can use the source command or simply . to source a file.

source is a Unix command that evaluates the file following the command, as a list of commands, executed in the current context. You can also use . for sourcing the file.
source my-script.sh;
. my-script.sh;
Both commands will have the same effect.
In contrast, passing the script filename to the desired shell will run the script in a subshell, not the current context.

Related

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.

How to set PATH envirnment variable through a shell script though that shell script gets terminated?

I am having a shell script ./my_shellscript.sh, its contents are as follows :
source /path/to/shell_script.sh
Where shell_script.sh contains:
export PATH=/path/to/a/dir:$PATH
which command_name
when I execute the my_shellscript.sh then it shows accurate path to a command, that I executed using "which" command?
When the shell_script.sh terminates and when I again do "which command_name" from command line it doesn't shows any path as it shown when I executed the scripts.
My question is that how to set/persist that path to environment variable [PATH:$PATH] though shell_script.sh terminates?
That's not how environments work, you can't change the parent environment. You can only change your environment, and (optionally) that of child processes to your process.
You could run your ./my_shellscript.sh with source (or .) to export it's variables to your current environment.
source my_shellscript.sh
or
. my_shellscript.sh
Other option to put the PATH variable extension into your .profile file in your home directory. (/home/your_username/.profile) That will be permanent.

In bash, what does dot command ampersand do?

I'm trying to understand a bash script I'm supposed to be maintaining and got stuck. The command is of this form:
. $APP_LOCATION/somescript.sh param1 param2 &
The line is not being called in a loop, not is any return code bening sent back to the calling script from somescript.sh
I know that the "." will make the process run in the same shell. But "&" will spawn off a different process.
That sounds contradictory. What's is really happening here? Any ideas?
The script is running in a background process, but it is a subshell, not a separately-invoked interpreter as it would be without the dot.
That is to say -- the current interpreter forks and then begins running the command (sourcing the script). As such, it inherits shell variables, not just environment variables.
Otherwise the new script's interpreter would be invoked via an execv() call, which would replace the current interpreter with a new one. That's usually the right thing, because it provides more flexibility -- you can't run anything but a script written for the same shell with . or source, after all, whereas starting a new interpreter means that your other script could be rewritten in Python, Perl, a compiled binary, etc without its callers needing to change.
(This is part of why scripts intended to be exec'd, as opposed to than libraries meant to be sourced, should not have filename extensions -- and part of why bash libraries should be .bash, not .sh, such that inaccurate information isn't provided about what kind of interpreter they can be sourced into).
TL;DR
. $APP_LOCATION/somescript.sh param1 param2 &
This sources a script as a background job in the current shell.
Sourcing a Script
In Bash, using . is equivalent to the [source builtin]. The help for the source builtin says (in part):
$ help source
source: source filename [arguments]
Execute commands from a file in the current shell.
In other words, it reads in your Bash script and evaluates it in the current shell rather than in a sub-shell. This is often important to give a script access to unexported variables.
Background Jobs
The ampersand executes the script in the background using job control. In this case, while the sourced script is evaluated in the context of the current shell, it is executed in a separate process that can be managed using job control builtins.

Difference between different ways of running shell script

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.

What is the difference between these two commands which are used to run shell script?

Here I have one script which exporting some necessary path in Linux. After running this script I have to run some other scripts.
I have two scripts
1 import.sh = importing paths
2 main.sh = this script do something with HCI (use for Bluetooth purpose).
when I run ./import.sh and than ./main.sh then it's giving error.
And when I run . ./import.sh and then ./main.sh then it's working fine.
So what is the diff between ./import.sh and . ./import.sh?
What happens if I run script as a super user? May be . ./ using for run script as a super user.
The difference between the two invocations is that ./import.sh is executing import.sh as a program, and . ./import.sh is evaluating it in your shell.
If "import.sh" were an ELF program (a compiled binary, not a shell script), . ./import.sh would not work.
If import.sh had a shebang at the top (like #!/bin/perl), you'd be in for a nasty surprise and a huge number of error messages if you tried to do . ./import.sh - unless the shebang happened to match your current shell, in which case it would accidentally work. Or if the Perl code were to somehow be a valid Bash script, which seems unlikely.
. ./import.sh is equivalent to source import.sh, and doesn't require that the file have the execute bit set (since it's interpreted by your already-running shell instead of spawned via exec). I assume this is the source of your error. Another difference is that ./import.sh runs in the current shell instead of a subshell, so any non-exported environment variables will affect the shell you used for the launch!
So, they're actually rather different. You usually want to ./import.sh unless you know what you're doing and understand the difference.
./import.sh executes the shell script in a new sub shell shell.
. ./import.sh executes the shell script in the current shell.
The extra . denotes the current shell.
./import.sh runs the script as a normal script - that is, in a subshell. That means it can't affect your current shell in any way. The paths it's supposed to import won't get set up in your current shell.
The extra ., which is equivalent to source, runs the script in the context of your current shell - meaning it can modify environment variables, etc. (like the paths you're trying to set up) in the current shell. From the bash man page:
. filename [arguments]
source filename [arguments]
Read and execute commands from filename in the current shell environment and return the exit status of the last command executed from filename.
The . ./import.sh "sources" the script, where as simply ./import.sh just executes it.
The former allows you to modify the current environment, where the later will only affect the environment within the child execution.
The former is also equivalent to (though mostly Bash-specific):
source ./import.sh
help source yields:
source: source filename [arguments]
Execute commands from a file in the current shell.
Read and execute commands from FILENAME in the current shell. The
entries in $PATH are used to find the directory containing FILENAME.
If any ARGUMENTS are supplied, they become the positional parameters
when FILENAME is executed.
Exit Status:
Returns the status of the last command executed in FILENAME; fails if
FILENAME cannot be read.

Resources