Source Vs Run

TLDR

# Runs the hi.sh script within the parent process.
# - parent process env variables are affected.
source /tmp/hi.sh

# Creates child process to run hi.sh script in.
# - parent process env variables are not affected
/tmp/hi.sh

PID: Process ID

PID

Details

Lets say you have a script /tmp/hi.sh

Which has the following ($$ prints PID (Process ID))

echo "My PID: $$"

If you run it as

/tmp/hi.sh

You will get PID that is different from parent process.

But if you run it as

source /tmp/hi.sh

You will get the same PID as your parent process.

Example code:

echo 'echo pid in /tmp/hi.sh: $$' > /tmp/hi.sh
chmod +x /tmp/hi.sh

echo "PID of parent process is: $$"
echo "Running /tmp/hi.sh"
/tmp/hi.sh

echo "Source /tmp/hi.sh"
source /tmp/hi.sh

Env Variables example

Env Variables. Example: source ./scratch2.sh (does change ENV variables) Lets say you have two files scratch.sh and scratch2.sh

Scratch Shell:

main() {
  SOME_ENV="Scratch1 value"
  echo "Running Scratch1, PID: $$"
  echo.green "SOME_ENV: ${SOME_ENV:?}"
  echo "Starting source:"
  echo "--------------------------------------------------------------------------------"
  source "${SCRATCH_SHELL2:?}"
  echo "--------------------------------------------------------------------------------"
  echo "Finished source."
  echo.green "SOME_ENV: ${SOME_ENV:?}"
}

main "${@}" || exit 1

Scratch 2 Shell:

main() {
  echo "Running Scratch2, PID: $$"
  SOME_ENV="some Scratch2 value"
}

main "${@}" || exit 1

When you run scratch 1 shell since you are using source env variables are affected leading to output:

Running Scratch1, PID: 27508
SOME_ENV: Scratch1 value
Starting source:
--------------------------------------------------------------------------------
Running Scratch2, PID: 27508
--------------------------------------------------------------------------------
Finished source.
SOME_ENV: some Scratch2 value
Env Variables, Example: ./scratch2.sh (does not change parent ENV variables)
# scratch.sh:
main() {
  SOME_ENV="Scratch1 value"
  echo "Running Scratch1, PID: $$"
  echo.green "SOME_ENV: ${SOME_ENV:?}"
  echo "Starting source:"
  echo "--------------------------------------------------------------------------------"
  "${SCRATCH_SHELL2:?}"
  echo "--------------------------------------------------------------------------------"
  echo "Finished source."
  echo.green "SOME_ENV: ${SOME_ENV:?}"
}

main "${@}" || exit 1
#  scratch2.sh: 
main() {
  echo "Running Scratch2, PID: $$"
  SOME_ENV="some Scratch2 value"
  echo.green "SOME_ENV: ${SOME_ENV:?}"
}

main "${@}" || exit 1

Output:

Running Scratch1, PID: 27577
SOME_ENV: Scratch1 value
Starting source:
--------------------------------------------------------------------------------
Running Scratch2, PID: 27578
SOME_ENV: some Scratch2 value
--------------------------------------------------------------------------------
Finished source.
SOME_ENV: Scratch1 value

Notice even though we set env variable in scratch2, the change was not reflected from perspective of scratch1 since it was a separate process.

Source Further Dive

Source to pipe

Piping output of source makes it so ENV variable changes are not reflected in parent process.

When you pipe you create a sub-process. Hence, you env variable changes would not propagate up to parent, since they were only changed in the child process.

Example Code
# scratch1.sh
main() {
  SOME_ENV="Scratch1 value"
  echo "SOME_ENV: ${SOME_ENV:?} (in Scratch1) (PID: $$, BASHPID: $BASHPID)"
  echo "--------------------------------------------------------------------------------Starting source"
  source "${SCRATCH_SHELL2:?}" | tee /tmp/out
  echo "--------------------------------------------------------------------------------Finished source"
  echo "SOME_ENV: ${SOME_ENV:?} (in Scratch1) (PID: $$, BASHPID: $BASHPID)"
}

main "${@}" || exit 1
# scratch2.sh
main() {
  SOME_ENV="Scratch2-value"
  echo "SOME_ENV: ${SOME_ENV:?} (in Scratch2) (PID: $$, BASHPID: $BASHPID)"
}

main "${@}" || exit 1


Children
  1. Source to Pipe