42

I was reading about positional parameters in Unix and then I found this info:

The shell allows a command line to contain at least 128 arguments; however, a shell program is restricted to referencing only nine positional parameters, $1 through $9, at a given time. You can work around this restriction by using the shift command.

So I created a simple shell script called file like the following:

#! /bin/bash
echo $14

then ran it like the following :

./file 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

and I got 14!

So how is that possible if the shell doesn't allow more than 10 parameters (from $0 to $9) without using shift command?

muru
  • 69,900
  • 13
  • 192
  • 292
Colin Jack
  • 603
  • 5
  • 9
  • 56
    ... try `./file a b c d e f g h i j k l m n o` – steeldriver Jan 25 '21 at 01:56
  • 22
    Ok , it gave me "a4" this is because it reads it as $1 then number 4 , aha now I get it I also replaced the number 14 in my example and it give the same result , now everything is clear i was using bad example , thanks a lot mate @steeldriver – Colin Jack Jan 25 '21 at 02:04
  • 14
    Most modern shells allow you to reference higher-numbered parameters with `${ }` (try `echo "${14}"`). – Gordon Davisson Jan 25 '21 at 02:16
  • 1
    ... indeed - see also [When to quote a parameter expansion, and enclose parameter name in braces?](https://unix.stackexchange.com/questions/421737/when-to-quote-a-parameter-expansion-and-enclose-parameter-name-in-braces) – steeldriver Jan 25 '21 at 02:21
  • 2
    Bash behaviour is documented at https://www.gnu.org/software/bash/manual/bash.html#Positional-Parameters. – berndbausch Jan 25 '21 at 06:14
  • 1
    @GordonDavisson That's standard behavior. Only very old pre-POSIX shells would choke on `${14}` (or `${014}`). The OP is reading outdated documentation. –  Jan 25 '21 at 07:38
  • 1
    _"The shell allows a command line to contain at least 128 arguments;"_ it does say "at least", but that's probably an outdated limitation too. At least on Linux, some 100000 args still work, e.g. `dash -c 'echo ${100000}' - {1..99999} xxx` gives `xxx`. I wouldn't say it works _fine_ with Bash though, on Bash 4.4. it takes ages to start when given so many args. – ilkkachu Jan 25 '21 at 11:06
  • 1
    Using a value equals to the variable name when testing something is always a bad idea – Jimmy R.T. Jan 26 '21 at 12:51
  • @steeldriver It's not often that I read a fragment of shell script and laugh out loud :-) – Mark Morgan Lloyd Jan 26 '21 at 17:15

1 Answers1

69

When you run

echo $14

what happens is that bash interprets the argument $14 as $1 and 4 separately. It then expands $1 (which in this case is equal to "1"), then appends the string 4 to it, which results in "14". Although that was the result you were expecting, it's actually a side effect from Bash's actual behaviour. Like @steeldriver mentioned in comments, running your script like this instead :

./file a b c d e f g h i j k l m n

and then calling echo $14 won't output "n" but "a4".

Note that wrapping the variable in double-quotes :

echo "$14"

will still not expand the variable correctly in Bash. The standard method is to use curly braces around the variable name :

echo ${14}

For more information, see the official documentation for parameter expansion in Bash. It can do a lot more cool things too, like

${14^^*}

to capitalize argument no.14. Give it a read! :)

D. Ben Knoble
  • 484
  • 3
  • 8
Umlin
  • 658
  • 6
  • 10
  • 9
    Note that that "bug" (unfortunately mandated by POSIX) where `$14` is `${1}4` instead of `${14}` was fixed in csh, ash, tcsh, rc, es, akanga and zsh (when not in sh/ksh emulation), though broken again in some ash derivatives for POSIX compliance. – Stéphane Chazelas Jan 25 '21 at 09:35
  • 2
    Just wrapping the variabled in double quotes does not work: `bash -c 'echo "$14"' - WTF` => `WTF4`. –  Jan 25 '21 at 09:59
  • 3
    If that worked for you, it's because you had used `dash` (Debian's `/bin/sh`) where simply `echo $14` would've worked too: `dash -c 'echo $14' - a b c d e f g h i j k l m n` => `n`. –  Jan 25 '21 at 10:01
  • @user414777 Indeed. I used bash but I didn't test it properly. Thanks for pointing this out, I'll edit my answer. – Umlin Jan 25 '21 at 10:10
  • @user414777, fwiw, that seems to be fixed in newer versions of Dash – ilkkachu Jan 25 '21 at 10:54
  • 4
    @ilkkachu, *fixed* or *broken* depending on how you want to look at it. As per my first comment here, it used to be fine but was *broken* (IMO) in newer versions for POSIX compliance. – Stéphane Chazelas Jan 25 '21 at 14:32
  • 2
    @StéphaneChazelas, well. As far as I understand, Dash is meant to implement the POSIX shell language as it is, instead of creating a new but actually sane language. So in that context, behaviour that's both noncompliant and different from almost all other POSIX-like shells seems unquestionably a bug. – ilkkachu Jan 25 '21 at 22:33