Why ${1+"$@"} is used in shell script
Tags: linux, bash, shell
Published: 2019-05-01 22:46:04

---
title: Why ${1+"$@"} is used in shell script
published: true
description: 
tags: linux, bash, shell
---

Sometimes, I use `${1+"$@"}` instead of `"$@"` when passing arguments to a function or an external command.
Here is the example.

```sh
#!/bin/bash

main () {
  # Something...
}

main ${1+"$@"}
```

`${1+"$@"}` behaves almost same as `"$@"`(If you do not understand `"$@"`, see [this helpful article](https://wiki.bash-hackers.org/scripting/posparams#all_positional_parameters)).

However, there are many shell scripts that include `${1+"$@"}` instead of `"$@"` all over the world.

Oops, have you never seen them ?

OK, let's execute this command on your machine.

```sh
$ grep -a -F '${1+"$@"}' /bin/* /usr/bin/*
```

You'll get bunch of results on either Linux or BSD.

```sh
$ grep -a -F '${1+"$@"}' /bin/* /usr/bin/*
/bin/c2ph:    eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
/bin/c89:exec gcc $fl ${1+"$@"}
/bin/c99:exec gcc $fl ${1+"$@"}
/bin/catchsegv:"$prog" ${1+"$@"} 2>&3 3>&-)
/bin/find2perl:    eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
...
...
/usr/bin/xzgrep:eval "set -- $operands "'${1+"$@"}'
/usr/bin/zgrep:    eval "set -- $arg2 "'${1+"$@"}'
/usr/bin/zgrep:eval "set -- $operands "'${1+"$@"}'
```

And I know some OSS projects use this expression.
In addition, I am also using it in my own projects.

* [tmux-xpanes](https://github.com/greymd/tmux-xpanes)
* [super_unko](https://github.com/unkontributors/super_unko/blob/master/bin/unko.tower#L141)

Let me note **my** reason of why I'd rather use `${1+"$@"}` than `"$@"`.
I am little bit concern about whether my purpose is reasonable or not.
Because historical background related to grammatical expression of code is very hard to find on the web (Because it's difficult to search).
If you use `${1+"$@"}` and have any other intention, please comment and let me know!

# My conclusion first

## Why use it?

* To improve portability
* `"$@"` does not work as expected on the particular environments.
    * Early versions of Bourne shell
    * Particular versions of Bash

## Is it better to use it?

Depending on your situation.

* You do not need to use `${1+"$@"}` if you can ensure that `"$@"` expands 1 or more positional parameters.
* It's better to use it if the number of positional parameters of `"$@"` can be 0.
* But I think, you should not pay large cost to use it.

# What `${1+"$@"}` does ?

This expression is came from the variable expansion of `${parameter+word}` provided by Bourne shell.

[Bourne Shell Manual, Version 7](https://www.in-ulm.de/~mascheck/bourne/v7/)
>   ${parameter+word}
> If parameter is set  then  substitute	 word;	otherwise
> substitute nothing.

`1` is located to `parameter`'s position, and `"$@"` is located to the `word`'s one.

As a result, `${1+"$@"}` means...
**If `$1` is defined, evaluate `"$@"`**.

This expression can be used in the not only Bourne shell, but also Bourne Again Shell (Bash).
It seems that this is not documented on the Bash's manual. But you can find it if you read carefully.

Bash has `${var:+word}` variable expansion and its colon can be omitted.

[Bash Reference Manual](https://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion)
> if the colon is omitted, the operator tests only for existence.

Normally, `${var:+word}` checks whether the `var` is empty or not.
But if colon is ommited, it just checks `var` is defined or not.
Therefore, it behaves completely same as Bourne shell's `${parameter+word}`

## What's happened with `"$@"` ?

Here is simple shell script. It just prints the arguments by `echo`.
If any undefined variable is used, the script exits unsuccessfully because `set -u` is stated.
But it seems that there is no undefined variable as you may know.

#### `myecho.sh`

```sh
set -u
echo "$@"
```

#### Bash 3.2

Try it on the old version of Bash first.

```
~ $ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
Copyright (C) 2007 Free Software Foundation, Inc.
```

It works as expected.

```sh
$ /bin/sh myecho.sh A B C
A B C

$ /bin/sh myecho.sh
### empty result
```


#### Bourne shell on V7

Next, let's test in on Bourne shell.
But what is "Bourne shell" ? (I do not know honestly)
Let's check Wikipedia :p

[Bourne shell - Wikipedia](https://en.wikipedia.org/wiki/Bourne_shell)
> The Bourne shell was the default shell for Version 7 Unix.

OK, let's use Version 7 Unix (called V7 in this article).
V7 can be simulated on [SIMH](http://simh.trailing-edge.com/) (I used the docker container [alpine-simh](https://hub.docker.com/r/rattydave/alpine-simh)).
Off course, `vi` is not installed to the V7 as `myecho.sh` must be created by `ed`.

```
$ ed myecho.sh
a
set -u
echo "$\@
"
.
w
17
q

$ cat myecho.sh
set -u
echo "$@"
```

And test it.

```
$ /bin/sh myecho.sh A C D
A B C

$ /bin/sh myecho.sh
myecho.sh: @: parameter not set
```

Exit with error !

But, is it big issue for you ?
Many of you may think that everybody no longer uses Bourne shell on V7.
But see next.

#### Bash 4.0.0

I built Bash 4.0.0.

```shell
$ /usr/bin/bash --version
GNU bash, version 4.0.0(1)-release (x86_64-unknown-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
```

And test this script.
Result is like this.

```shell
$ /usr/bin/bash myecho.sh A B C
A B C

$ /usr/bin/bash myecho.sh
myecho.sh: line 2: $@: unbound variable
```

Exit with error again !
Particular version of Bash does not work as well.

However, using `${1+"$@"}` instead of `"$@"`, we can avoid above errors.

## History of this issue

By the way, who started to use `${1+"$@"}` ?
I am not sure, but there is the discussion in the book called [The UNIX-HATERS Handbook](https://web.mit.edu/~simsong/www/ugh.pdf) published in 1994.
Regarding this book, shell experts had used this expression as of 1991.

> Milt> what does the “${1+“$@”}” mean? I’m sure it’s to
> Milt> read in the rest of the command line arguments, but
> Milt> I’m not sure exactly what it means

It says the reason is ...

> If we used only “$@” then that would substitute to “” (a null argu-
> ment) if there were no invocation arguments, but we want no argu-
> ments reproduced in that case, not “”.

Let me explain this part in detail.
Generally (and grammatically), `"$@"` is supposed to be handled as NOTHING if the number of arguments is 0.

```bash
my_command "$@"

### Same as ...
my_command
```

However, `"$@"` is handled as empty string `""` on the particular environments like Bourne shell on V7.

```bash
my_command "$@"

### Same as this !
my_command ""
```

This is unexpected behavior for many developers, I guess.
On the other hand, `${1+"$@"}` can be NOTHING as expected.

```bash
my_command ${1+"$@"}

### same as below
my_command
```

And the book says that this way is compatible with V7.
> I think ${1+“$@”} is portable all the way back to “Version 7 Unix.”

### Investigation on V7

I checked this behavior on V7.
Firstly, create two files as followings.

#### `main1.sh`

```
./sub.sh "$@"
```

#### `sub.sh`

```
echo "# = $#"
echo "@ = $*"
```

( I could not unify them into single file as shell's "function" was not suppored as of V7. )

The result of `main1.sh` is like this.

```
$ /bin/sh main1.sh A B C
# = 3
@ = A B C

$ /bin/sh main1.sh
# = 1
@ =
```

`$#` is 1. Because empty string `""` is passed.


Next, create `main2.sh` that uses `${1+"$@"}`  instead of `$@`.

#### `main2.sh`

```text
./sub.sh ${1+"$@"}
```

The result is ...

```text
$ /bin/sh main2.sh A B C
# = 3
@ = A B C

$ /bin/sh main2.sh
# = 0
@ =
```

`$#` is 0.
Because NOTHING is passed.

## Investigation on Bash 4.0.0

Personally, to keep compatibility with particular versions of Bash is main reason to use this expression.

As I mentioned above, `"$@"` is not equal to `${1+"$@"}` in Bash 4.0.0 as same as Bourne shell.

(I noticed this bug when I was running automated testing for my personal project.)

Strangely, `"$@"` is going to be NOTHING.
This is different from Bourne shell.

```text
$ /usr/bin/bash main1.sh
# = 0
@ =
```

```text
$ /usr/bin/bash main2.sh
# = 0
@ =
```

However, the script is going to be failed with `set -u` as Bash recognizes that unbound variable is used.

```sh
$ /usr/bin/bash -u main1.sh
main1.sh: line 1: $@: unbound variable

$ /usr/bin/bash -u main2.sh
# = 0
@ =
```

This bug can also be avoided by `${1+"$@"}`.
It was already fixed in later versions.
But I know Bash had got many bugs historically.
For example, parameter expansion still has bugs as of bash 5.0.3.

```sh
$ bash -c 'set -u; typeset -a a; echo "${#a[@]}"'
bash: a: unbound variable
```
(this bug was already reported by [@satoh_fumiyasu](https://twitter.com/satoh_fumiyasu/status/883228541457899520))

The reason why I am still using `${1+"$@"}` is mainly came from the concerns about the Bash's robustness.
Honestly, I am not interested in the compatibility for early version of Bourne shell (It does not support even the Shebang !).

# Conclusion

As I mentioned, `${1+"$@"}` might NOT be equal to `"$@"` if the number of positional parameters is 0.
Therefore, it's not necessary to use if the number of parameter is supposed to be 1 or more (You would rather NOT use it as it will cause confusion).

If the number of parameters can be 0, it's worth using the expression.
Bash can recognize it grammatically.

However, the environment that has this issue is quite rare as of 2019.

* This behavior is fixed as of 1986 in SVR3 shell (I guess).

[Bourne shell - Wikipedia](https://en.wikipedia.org/wiki/Bourne_shell)
> Features introduced after 1979
> ...
> Modern "$@" – SVR3 shell (1986)

* Almost all the Bash versions does not has this issue as far as I know.

* Actually, [Shell Coding Style guide provided by Google](https://google.github.io/styleguide/shell.xml) at 2013 recommends to use `main "$@"` ([link](https://google.github.io/styleguide/shell.xml?showone=main#main)).

Off course, there is no technical concern about use of `${1+"$@"}`.
On the other hand, I think it is not worth paying attention and large cost to adopt this expression.