11

When I type this in bash command line

$ x=hi; printf '%s ' "$x" "${x[0]}"; echo "${_[0]}"

I have this output:

hi hi hi
  • Why "${_[0]}" turns out to be "hi" in the output?
  • Why could we use the "x[0]" syntax, given that "x"is just a string instead of an array?
19

The _ parameter has several meanings depending on context, but it is never an array. Likewise, in your example, x is not an array. The reason you're able to treat it as if it is an array is that Bash allows non-array variables to be treated as if they were one-element arrays. Bash likewise allows array variables to be treated as if they were non-arrays, giving the first element.

As man bash says:

Referencing an array variable without a subscript is equivalent to referencing the array with a subscript of 0. Any reference to a variable using a valid subscript is legal, and bash will create an array if necessary.

So "${_[0]}" behaves the same as "${_}" or "$_", because _ is not an array. Likewise, "${x[0]}" behaves the same as "${x}" or "$x", because x is not an array.


As for why _ holds the value hi: In the usage you've shown, performing parameter expansion on the special _ parameter yields the last argument of the most recent (synchronously executed) command.

As man bash says of _:

At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list. Subsequently, expands to the last argument to the previous simple command executed in the foreground, after expansion. Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command. When checking mail, this parameter holds the name of the mail file currently being checked.

(emphasis mine)

In this case, the most recently executed command was:

printf '%s ' "$x" "${x[0]}"

The arguments passed to printf were:

  • %s , on which only quote removal was performed.
  • hi, on which parameter expansion was performed, followed by quote removal.
  • hi, on which a more complex form of parameter expansion yielding the same result was performed, followed by quote removal.
| improve this answer | |
13

$_ is a special parameter. It is used in a number of ways but, in your case, it refers to the last argument of the previous command:

$ echo hi Hi Hello
hi Hi Hello
$ echo "$_"
Hello

Since $_ is a variable, not an array, the syntax ${_[0]} is just useless clutter that accesses $_:

$ echo "$_"
Hello
$ echo "${_[0]}"
Hello

Documentation

From man bash:

$_
At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list. Subsequently, expands to the last argument to the previous simple command executed in the foreground, after expansion. Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command. When checking mail, this parameter holds the name of the mail file currently being checked.

I highlighted in bold the part relevant to your usage.

| improve this answer | |
5

As others have mentioned ${_[0]} is extra typing for ${_} which in turn can be abbreviated into $_ as it is most commonly used.

As mentioned previously it is a variable that contains the last parameter of the last command used. A practical application is like this:

$ ll /etc/lsb-release
-rw-r--r-- 1 root root 105 Feb 20  2019 /etc/lsb-release

$ cat $_
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.6 LTS"

In the first command the last parameter is /etc/lsb-release. In the second command the parameter is $_, which repeats /etc/lsb-release so you don't have to retype it.

| improve this answer | |

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.