Shell – Using Parameter Variables ($1, …, $@) Directly in CLI

parametershell

Sometimes it is necessary to emulate and verify the above variables in small examples and then can be copied immediately to some script, etc.

I tried to solve by using a simple example in the following ways:

(find $1) /tmp
sh -c '(find $1) /tmp'
sh -c "find $1" /tmp
echo '(find $1) /tmp' | sh

and with other combinations. Also experimented by adding the shebang interpreter directive #!/bin/sh -x, but did not get the desired result.

Can I do this simply?

Best Answer

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.

Related Question