How to pass arguments to a script invoked by source command?

I am invoking a script through source command and want to pass arguments to the script.

I have checked man source, the bash returns:

: [arguments]
No effect; the command does nothing beyond expanding arguments and performing any specified redirections. A zero exit code is returned.

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. If filename does not contain a slash, file names in PATH are used to find the directory containing filename. The file searched for in PATH need not be executable. When bash is not in posix mode, the current directory is searched if no file is found in PATH. If the sourcepath option to the shopt builtin command is turned off, the PATH is not searched. If any arguments are supplied, they become the positional parameters when filename is executed. Otherwise the positional parameters are unchanged. The return status is the status of the last command exited within the script (0 if no commands are executed), and false if filename is not found or cannot be read.

It has no examples, so I don't understand it .

4 Answers

Create a file test.sh with the following contents:

echo "I was given $# argument(s):"
printf "%s\n" "$@"

and then source it from an interactive shell session:

$ source ./test.sh a 'b c'
I was given 2 argument(s):
a
b c

so you access the arguments just like you would do in a regular bash script, with $@ or $1, $2, $3, etc.

For comparison, run it as a regular script:

$ bash ./test.sh a 'b c'
I was given 2 argument(s):
a
b c
4

Expanding on the (currently accepted) answer from redneb...

TL;DR Need to source a script with no positional arguments within another script? try function DoSource() { source test.sh ; } ; DoSource instead of just source test.sh.

sourceing a script with no arguments within another script

The excerpt from Bash manual in the question shows the details of how the positional parameters are assigned to the sourced script. In particular, if the source command does not specify any argument, it is assigned the ones from the calling environment.

A consequence is that it may be troublesome to source a script passing no arguments within another script. For example, let's use redneb example as test.sh:

echo "I was given $# argument(s):"
printf "%s\n" "$@"

sourced within another script userScript.sh, which is e.g. the one-liner:

source test.sh

When running an example like above:

$ bash userScript.sh a 'b c'
I was given 2 argument(s):
a
b c

test.sh inherits userScript.sh positional arguments... which now is not what I want (if I did, I could have used source test.sh "$@").

I have found the following to be a useful workaround: encapsulate the source command into a Bash function. The new userScript.sh looks like:

function DoSource() { source test.sh ; }
DoSource

reports:

$ bash userScript.sh a 'b c'
I was given 0 argument(s):

Note that specifying an empty argument (source test.sh '') is not equivalent, since the empty argument would be passed to test.sh.

If the sourcing script is also expected to be sourced

If userScript.sh itself is supposed to be sourced, then one probably does not want to leave DoSource() around. In that case, the simple solution is self destruction:

function _userScript_sh_DoSource() { source test.sh ; unset "$FUNCNAME" ; }
_userScript_sh_DoSource

for one-time-only usage (function name has been chosen to reduce the chance of name conflicts); or a unset _userScript_sh_DoSource command can be places after _userScript_sh_DoSource is not needed any more.

Flexible variant for multiple use

A more complex variant of DoSource():

function DoSource() { local ScriptName="$1" ; shift ; source "$ScriptName" ; }
DoSource test1.sh
DoSource test1.sh "$@"
DoSource test2.sh

can be used as a "drop-in" replacement for source, with the only difference being that when no positional argument is specified for the script to be sourced, source inherits them, while DoSource uses none. Note however that DoSource is a function and as such behaves differently from source in other aspects (e.g. stack call, FUNCNAME, ...).

Bourne shell and some other shells ignore the parameters passed to "." (see ).

I can propose one workaround. Let imagine the script which you want to source looks like this:

$ cat ./setEnv
export BRANCH=$0
export TARGET=$1

The usual way it does not work:

$ sh -c '. ./setEnv master linux; echo BRANCH=$BRANCH ; echo TARGET=$TARGET'
BRANCH=sh
TARGET=

But if you specify the parameters after the ' ' then they will belong to the parent shell that is they will be accessible in the script which you are sourcing:

$ sh -c '. ./setEnv; echo BRANCH=$BRANCH ; echo TARGET=$TARGET' master linux
BRANCH=master
TARGET=linux

I use this way when I need a oneline-command in Bourne shell which starts from ". ./setEnv" and continues with multiple commands separated with ; or &&.

I stumbled over another trap

test.sh

echo "I was given $# argument(s):"
printf "%s\n" "$@"
source test2.sh

test2.sh

echo "I was given $# argument(s):"
printf "%s\n" "$@"

now call

>bash test.sh a b

output:

I was given 2 argument(s):
a
b
I was given 2 argument(s):
a
b

This might be a problem if test2.sh evaluates arguments resp. argument count. To avoid this you can for example shift the args away.

test.sh

echo "I was given $# argument(s):"
printf "%s\n" "$@"
for arg in "$@"; do shift; done
source test2.sh

EDIT:

Just noticed already covered this topic.

See also Avoid command line arguments propagation when sourcing bash script

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like