The first argument after sh -c inline-script
goes to $0
(which is also used for error messages), and the rest go in $1
, $2
...
$ sh -c 'blah; echo "$0"; echo "$1"' my-inline-script arg
my-inline-script: blah: command not found
my-inline-script
arg
So you want:
sh -c 'find "$1"' sh /tmp
(in the olden days, you could find sh
implementations where the first arg went into $1
instead, so you would do:
sh -c 'find "$1"' /tmp /tmp
Or:
sh -c 'shift "$2"; find "$@"' sh 3 2 /tmp1 /tmp2
to account for both behaviours, but those shells are gone now that POSIX is prevalent and publicly available).
If you want to set $1
, $2
in a local scope within the current shell, that's where you'd use functions. In Bourne-like shells:
my_func() {
find "$1"
}
my_func /tmp
Some shells support anonymous functions. That's the case of zsh
:
(){find "$1"} /tmp
Or es
:
@{find $1} /tmp
To change the current positional parameters, permanently, the syntax is shell dependant. dchirikov has already covered the Bourne-like shells (Bourne, Korn, bash
, zsh
, POSIX, ash
, yash
...).
The syntax is:
set arg1 arg2 ... argn
However, you need:
set --
To empty that list (or shift "$#"
) and
set -- -foo
to set $1
to something starting with -
or +
, so it's a good habit to always use set --
especially when using arbitrary data such as set -- "$@" other-arg
to add arguments to the end of the positional parameter list.
In shells of the csh
family (csh
, tcsh
), you assign to the argv
array:
set argv=(arg1 arg2)
In shells of the rc
family (rc
, es
, akanga
), to the *
array:
*=(arg1 arg2)
Though you can also assign elements individually:
2=arg2
In fish
, the positional parameters are in the argv
array only (no $1
, $@
there):
set argv arg1 arg2
In zsh
, for compatibility with csh
, you can also assign to the argv
array:
argv=(arg1 arg2)
argv[4]=arg4
And you can also do:
5=arg5
That means you can also do things like:
argv+=(another-arg)
to add an argument to the end, and:
argv[-1]=()
argv[2]=()
to remove an argument from the end or the middle, which you can't easily do with other shells.
In
. path/to/script at least one argument
As others have said, depending on the shell, those extra arguments are either ignored (Bourne, Almquist) or replace the current set of positional parameters ($@
, $1
, $2
...) for the interpretation of that script (most other shells). POSIX doesn't specify the behaviour when extra arguments are passed to .
. In that case, there is also some variation between shells as to whether any change to the positional parameters (like with set a b
) made within the script will remain after .
finishes.
In:
. path/to/script
however, the behaviour is specified. The positional parameters are required not to be affected, so will stay the same as before for the interpretation of the script.
If you want the positional parameters to be an empty list during the interpretation of that script, you'll need to reset it before hand, or use a function:
set -- # or shift "$#"
. path/to/script
(though the positional parameters are lost). Or use a function:
dot() { file=$1; shift; . "$file"; }
dot path/to/myscript
At the time the .
command is executed, the positional parameters will be that of the function. After the shift
, it will be the arguments passed to the function minus the first one, so in the above, an empty list; but that means that with that function you can portably pass an arbitrary list of positional parameters to the sourced script as in:
dot path/to/myscript extra args
Where those extra
args
will be available as $1
and $2
within the sourced script.
And if myscript
calls set
to modify the list of positional parameters, that will only affect the function's positional parameters, and those changes will not persist after the function returns.
Best Answer
There's just about one new syntax element per line, nice...
I'll annotate each line with the relevant section from
man bash
- may be helpful as is, or in combination with another answer:From the argument
$1
, cut out 1 char starting at 0 and check it's a/
:if [ ${1:0:1} = '/' ]
Leave char 0 out and get chars from 1 to the end from
$1
:tmp=${1:1} # Strip off leading '/' . . .
See section above, first case.
For arguments like
--foo=bar
, cut off text matching '=*' from the right, as much as possible to the left (think of handling--foo=bar=baz
):parameter=${tmp%%=*} # Extract name.
For arguments like
--foo=bar
, cut off text matching '*=' from the left, as much as possible to the right (think of handling--foo=bar=baz
):value=${tmp##*=} # Extract value.
(Note: the example case
--foo=bar=baz
is not supported as--foo
andbar=baz
, but as--foo
andbaz
)Source: section Parameter Expansion in
man bash
,man bash | less '+/Parameter Expansion'
(or, shorter
man bash | less '+/##'
)