How does a subshell's executed lines get printed to the main shell without running the source command? - linux

Let's say I have an executable shell script called foo.sh. Inside it is a simple echo "Hello World". From my understanding, when I run this via ./foo.sh, a subshell is invoked which executes the echo "Hello World" line.
Why, then, do I see the output of the echo command in my main shell/terminal? I would think you'd have to do a "source ./foo.sh" instead of the simple "./foo.sh" to see the output in your current shell.
Can any of you help clarify?

The standard output is inherited. Quoting from Bash Reference Manual:
Command Execution Environment
When a simple command other than a builtin or shell function is to be
executed, it is invoked in a separate execution environment that
consists of the following. Unless otherwise noted, the values are
inherited from the shell.
the shell’s open files, plus any modifications and additions specified by redirections to the command
...

Related

Difference between ways in script execution [duplicate]

I know that source and . do the same thing, and I would be surprised to learn if the other pairs of commands in the title don't so the same thing (because I'm running bash as my shell, $SHELL [script] and bash [script] are equivalent, right??).
So what's the difference between the three methods of executing the script? I'm asking because I just learned that sourcing a script is NOT the exact same as executing it. In a way that I didn't find obvious from running my "experiments" and reading the man pages.
What are the other subtle differences that I couldn't find by blindly calling these functions on incredibly simple scripts that I've written? After reading the above-linked answer, I can strongly guess that the answer to my question will be quite a simple explanation, but in a way that I'd almost never fully discover by myself.
Here's the "experiment" I did:
$. myScript.sh
"This is the output to my script. I'd like to think it's original."
$source myScript.sh
"This is the output to my script. I'd like to think it's original."
$bash myScript.sh
"This is the output to my script. I'd like to think it's original."
$$SHELL myScript.sh
"This is the output to my script. I'd like to think it's original."
$./myScript.sh
"This is the output to my script. I'd like to think it's original."
$myScript.sh
"This is the output to my script. I'd like to think it's original."
. script and source script execute the contents of script in the current environment, i.e. without creating a subshell. On the upside this allows script to affect the current environment, for example changing environment variables or changing the current work directory. On the downside this allows script to affect the current environment, which is a potential security hazard.
bash script passes script to the bash interpreter to execute. Whatever shebang is given by script itself is ignored. ("Shebang" referring to the first line of script, which could e.g. read #!/bin/bash, or #!/usr/bin/perl, or #!/usr/bin/awk, to specify the interpreter to be used.)
$SHELL script passes script to whatever is your current shell interpreter to execute. That may, or may not, be bash. (The environment variable SHELL holds the name of your current shell interpreter. $SHELL, if running bash, is evaluated to /bin/bash, with the effect detailed in the previous paragraph.)
./script executes the contents of a file script in the current work directory. If there is no such file, an error is generated. The contents of $PATH have no effect on what happens.
script looks for a file script in the directories listed in $PATH, which may or may not include the current work directory. The first script found in this list of directories is executed, which may or may not be the one in your current work directory.

Get full command from shell script

I'm looking for a way to access the full command from shell script, e.g.
Assume I have a script called test.sh. When I run it, the command line is passed to ruby as is (except the script itself is removed).
$ test.sh print ENV['HOME']
Is equivalent to
$ ruby -e "print ENV['HOME']"
When you run:
test.sh print ENV['HOME']
...then, before test.sh is started, the shell runs string-splitting, expansion, and similar processes. Thus, what's eventually run is (assuming no glob expansion):
execvp("test.sh", {"test.sh", "print", "ENV[HOME]"});
If you have a file named ENVH in the current directory, the shell may treat ENV['HOME'] as a glob, expanding it by replacing the glob expression with the filename, and thus running:
execvp("test.sh", {"test.sh", "print", "ENVH"});
...in any event, what exists on the other side of the execv*-series call done to run the new program has no information which was local to the original shell -- and thus no way of knowing what the original command was before parsing and expansion. Thus, it is impossible to retrieve the original string unless the outer shell is modified to expose it out-of-band (as via an environment variable).
This is why your calling convention should instead require:
test.sh "print ENV['HOME']"
or, allowing even more freedom from shell quoting/escaping syntax, passing program text via stdin, as with:
test.sh <<'EOF'
print ENV['HOME']
EOF
Now, if you want to modify your shell to do that, I'd suggest a function that exposes BASH_COMMAND. For instance:
shopt -s extdebug
expose_command() {
export SHELL_COMMAND="$BASH_COMMAND"
return 0
}
trap expose_command DEBUG
...then, inside test.sh, you can refer to SHELL_COMMAND. Again, however: This will only work if the calling shell had that trap configured, as within a user's ~/.bashrc; you can't simply put the above content in a script and expect it to work, because it's only the interactive shell -- the script's parent process -- that has access to this information and is thus able to expose it.

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.

difference between #./ and #. ./

whats the difference between executing script like
# ./test
and
# . ./test
test is simple script for example
#!/bin/bash
export OWNER_NAME="ANGEL 12"
export ALIAS="angelique"
i know the results but im unsure what actually happens
Thanks
./foo executed foo if it's marked as executable and has a proper shebang line (or is an ELF binary). It will be executed in a new process.
. ./foo or . foo loads the script in the current shell. It is equal to source foo
With your example code you need to use the second way if you want the exported variables to be available in your shell.
With the dot alone, bash is "sourcing" the specified file. It is equivalent to the source builtin and attempts to include and execute the script within the same shell process.
The ./ starts a new process, and the current shell process waits for it to terminate.
The first implies that the script (or binary) be executable. With the script (possibly) containing a shebang line telling which interpreter to use.
The second is a short-hand for "execute [argument] as a shell script". The file passed as argument does not need the executable bit set.

Resources