7

I have read that positional parameters start at $1 (for example: $1, $2, $3 and so on are positional parameters). But $0 is not a positional parameter.

But why $0 is not a positional parameter?

I think this could be a reason, but not sure:

Positional parameters only take their values when a script is executed. For example: if we do ./myScript Hello, then $1 will have the value Hello. But $0 can take its value on two occasions: when a script is executed (it would have the value of the script name), and when bash itself is executed without a script (it would have the value bash or -bash).

5
  • 3
    "Positional parameters only take their values when a script is executed." ... no ... look at set - x y z.
    – user62916
    Dec 23, 2017 at 19:22
  • 1
    Functions also have their own positional parameters.
    – nxnev
    Dec 23, 2017 at 19:34
  • I wonder if this should be tagged with history
    – ilkkachu
    Dec 23, 2017 at 20:05
  • 1
    You answered your self, $0 is the name of the script, the object that the parameters are applied to. You if you run ls -l ./dog, then the parameters are ls, -l, and ./dog, numbered 0, 1, and 2. Dec 23, 2017 at 22:43
  • $0 is a parameter required by the shell and not by the function or script (unless we are talking introspection or recursion). The over-arching point is that when you execute a function or script, it is the the shell that does the work. The contents of $1...9 are not interpreted by the script, they are handed to it by the shell which has a list starting at $0.
    – bu5hman
    Dec 24, 2017 at 4:14

3 Answers 3

15

@ikkachu has explained it better than I even could. I'll just add a history note.

The shell that came with the first versions of Unix (later named the Thompson shell) had no variable, but you could already write simple scripts that would take parameters.

     sh [ name [ arg1 ... [ arg9 ] ] ]

The name is the name of a file which will be read and in‐
terpreted.   If  not given, this subinstance of the shell
will continue to read the standard input file.

In command lines in the  file  (not  in  command  input),
character  sequences of the form "$n", where n is a digit
0, ..., 9, are replaced by the nth argument to the  invo‐
cation of the shell (argn).  "$0" is replaced by name.

$1...$n were already the arguments and $0 the name of the script (not the 1st argument) but were not called positional parameters then.

Note that back then, $1 was literally replaced with the first argument before shell interpretation.

For instance, a script that had:

echo $1

called as

sh script 'foo; echo bar'

would run echo foo; echo bar. That shell was a very simple one written for computers with few hundred kilobytes of memory.

The Bourne shell came almost a decade later (in the late 70s) with a version of Unix that introduced the environment and other goodies.

The Bourne shell did come with variables and a lot more programming constructs.

The positional parameter terminology, at least when it comes to Unix shells, was introduced by the Bourne shell, and referred to the same thing, $1... $n for the arguments of the script (and $0 still the script name). Like in the Thompson shell, you could only refer to the first 9 arguments ($1 to $9) with positional parameters (but use shift or "$@" or for i do loops to access the rest) (and that also explains (backward portability) why you need ${10} instead of $10 in most modern sh implementations for the 10th ones).

This time, sh script 'foo; echo bar' no longer caused echo bar to be run, but still the Bourne shell introduced that infamous split+glob, I suppose not to break backward compatible too much with the Thompson shell so that script 'foo *' where script has echo $1 still did call echo with foo and the list of files in the current directory as arguments (like in the Thompson shell but this time through a different mechanism).

scripts were also called shell procedures (note that the Bourne shell didn't have functions yet):

2.0 Shell procedures

The shell may be used to read and execute commands contained
in a file.  For example,

         sh file [ args  ]

calls the shell to read commands from file.  Such a file  is
called  a  command  procedure or shell procedure.  Arguments
may be supplied with the call and are referred  to  in  file
using  the  positional parameters $1, $2...

Functions were first introduced in the Korn shell (based on the Bourne shell) in the early 80s with a

function foo {
  ...
}

syntax.

Functions were eventually added to the Bourne shell as well later in SysVR2 (1984) with a different syntax:

foo() any-command

(but with unexpected behaviour when any-command was a simple command and had redirections which is probably the reason why POSIX only requires compound commands (like { ...; } the most often used one) be recognised there for the POSIX sh).

In both the Korn and Bourne shell, $0 in the function was still the script's name, not the function name (while the $1, $2 positional parameters refer to the function arguments).

That changed in ksh93 for the function f { style of function definition where $0 becomes the function name in the function.

In zsh, $0 is the function name like in ksh93. zsh also introduced anonymous functions with either the:

function { echo $1, $2; } foo bar

or

(){ echo $1, $2; } foo bar

syntax, in which $0 becomes (anon) (or stays the script's name with set +o functionargzero like when in sh/ksh emulation).

In zsh, like in csh, the arguments of the scripts are also in the $argv array, which lifts the confusion about the program name named similarly to the arguments.

There, you can assign values to the position parameters with:

argv[1]=value

or

1=value

(and also 0=newprogramname to change the program name).

While in other Bourne-like shells, you need to use set to assign all at once:

set -- arg1 arg2

And you can't change $0.

In rc (at least the Unix port), you can't do:

1 = value

But you can do:

* = (arg1 arg2)

to set the positional parameters. You can't change $0 in rc, but you can in its derivative es with 0=newprogramname like in zsh.

TL;DR

The $1, $2... to refer to the scripts arguments comes from the Thompson shell but were not called positional parameters yet. And $0 (probably chosen in reference to argv[0] as @ikkachu well put it) refers to the script name.

The positional parameters term comes from the Bourne shell.

$0 is not a positional parameter because it doesn't refer to an argument of the script. It refers to the script's name (or the shell's argv[0] when the shell doesn't execute any script).

11

There's a clear parallel from the numbered parameters ($0, $1, ...) to argv[], the array that contains the command line parameters when a process starts. The first element of the array, argv[0], usually holds the name of the process, and the actual arguments start from argv[1].

(Usually. It doesn't have to. The description of execve(2) states: "The value in argv[0] should point to a filename string that is associated with the process being started")

At least post-facto, it's easy to imagine the convention was just copied directly to the shell.

The values aren't directly copied, though. At least on my systems the shell process that starts when running ./script.sh with the hashbang #!/bin/bash -x gets the parameters /bin/bash, -x, ./script.sh. That is, the value that goes to $0 as seen by the script, is in argv[2] of the shell process.

I assume most people would consider the command name distinct from its parameters, so $0 is functionally different from the others, so it's not unreasonable to call it differently, too.

Of course we could have a scripting language that had a different naming convention. Perl puts the program name in a variable called $0, and the arguments in the array @ARGV, starting from index zero, i.e. $ARGV[0] etc.


Anyway, the most obvious answer would be to say that $0 is not a positional parameter, because the standard says so:

Under 2.5 Parameters and Variables

2.5.1 Positional Parameters

A positional parameter is a parameter denoted by the decimal value represented by one or more digits, other than the single digit 0.

2.5.2 Special Parameters

# Expands to the decimal number of positional parameters. The command name (parameter 0) shall not be counted in the number given by '#' because it is a special parameter, not a positional parameter.

0 (Zero.) Expands to the name of the shell or shell script.

4

$0 actually is a positional parameter (as well denoted in Michael Homer comments below, POSIX says "... giving the names of the arguments as positional parameters numbered 1 to n, and the name of the command (or in the case of a function within a script, the name of the script) as the positional parameter numbered 0".

It is the parameter of, as one could expect, position 0, which represents the name of the invoking command.

This is very useful as it allows the same script to behave differently depending on its invoking name, thus allowing the same executable to have multiple functions (think of symlinks, for example). This allows the process to know what is its invoking name.

If you give a quick look in your /bin, you will see many examples of this like bzcat, bzcmp, bzip2, ...; mdir, mcat, mcd, mdel, ...; xz, xzcat, unxz and many others. The way the process knows what function to perform is by checking the positional parameter 0.

Note: Some shells man pages may handle the terminology differently: bash's man page says it is not a positional parameter, ksh's man page (and the POSIX definition) say it is. Regardless of any terminology discussion, in both (bash and ksh), $0 contains the name of the invoking command or script. In case this is an interactive shell, $0 will contain the name of the shell (bash, ksh, ...), as it is the process name.

13

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.