46

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 4

52

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
Sign up to request clarification or add additional context in comments.

5 Comments

script != bash :(. This will not work on POSIX compliant shells (dash).
The question is tagged bash; a POSIX-compliant answer isn't required.
While it was not required, the heads up was appreciated: Must of us lives in a mixed shell environment and it's good to know that this are not POSIX compliant, but BASH(/ZSH/KSH/...) only.
The question may be tagged bash, but for those of us who need to write POSIX shell scripts, it's already difficult enough to find non-bash answers. And, really, a (reasonable recent) bash isn't that ubiquitous, unfortunately (see MacOS Darwin). I really appreciate POSIX shell solutions when they are provided.
Too bad that shells such as dash are so barebones they don't even pass their arguments in $NUMBER cant seem to find that info and keep running into these posts... ;o;
17

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, ...).

Comments

4

Bourne shell and some other shells ignore the parameters passed to "." (see https://unix.stackexchange.com/questions/5024/passing-variables-to-a-bash-script-when-sourcing-it).

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 &&.

Comments

1

I stumbled over another trap

test.sh

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

test2.sh

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

now call

>bash test.sh a b

output:

test.sh: I was given 2 argument(s):
a
b
test2.sh: I was given 2 argument(s):
a
b

When no argument is specified for the sourced script it takes over the arguments from the caller script.

This might be a problem if test2.sh evaluates arguments resp. argument count differently but you need to call test2.sh without arguments. To avoid this you can for example shift the arguments away.

test.sh

echo "I was given $# argument(s):"
printf "%s\n" "$@"
#if needed later back up $0 (resp $1, $2, ...) here
for arg in "$@"; do shift; done
source test2.sh

EDIT:

Just noticed https://stackoverflow.com/users/4633275/hamlet already covered this topic.

See also Avoid command line arguments propagation when sourcing bash script

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.