Why do I need to use cd "$@" instead of cd "$1" when writing a wrapper for cd?

5,667

Solution 1

Because, according to bash(1), cd takes arguments

   cd [-L|[-P [-e]] [-@]] [dir]
          Change  the  current  directory to dir.  if dir is not supplied,
          ...

so therefore the directory actually may not be in $1 as that could instead be an option such as -L or another flag.

How bad is this?

$ cd -L /var/tmp
$ pwd
/var/tmp
$ cd() { builtin cd "$1"; }
$ cd -L /var/tmp
$ pwd
/home/jhqdoe
$ 

Things could go very awry if you end up not where you expect using cd "$1"

Solution 2

Using "$@" will pass all arguments to cd where as $1 will only pass the first argument.

In your examples

$ . cdtest.sh "r st"

always works as you only pass in one argument, but if you were to pass in a flag as well such as

$ . cdtest.sh -L "r st"

Then only "$@" will execute correctly where "$1" will expand to cd -L losing the directory entirely.

However

$ . cdtest.sh r st

Fails in both cases as you are passing two parameters to cd, r and st which is not a valid way to execute cd. Parameters are separated by spaces which must be quoted (as in your first example) or escaped (r\ st) to be treated as one argument.

In the case of cd however it is very uncommon to pass in flags and you cannot pass in multiple directories so you will not see the difference in a real world use of either "$1" or "$@" for cd. But for other commands you will notice a difference so it is best practice to always use "$@" when you want to create a wrapper function or script like this.

Solution 3

There's also the case when there are no arguments:

$ cd /tmp; cd; pwd
/home/muru
$ cd_func() { builtin cd "$1"; }
$ cd_func /tmp; cd_func; pwd
/tmp

cd without any arguments changes to the home directory. Without any arguments, "$@" expands to nothing, but "$1" expands to the empty string. These are different:

$ args() { for i in "$@"; do echo "|$i|"; done; }
$ args
$ args ""
||

Solution 4

Arguments to a bash script are space delimited. $1 is the first argument. In your examples...

In example 1, $1 is the string "r st" ... in the second example, $1 is the one character string 'r' ...

$@ is all the arguments.

Share:
5,667

Related videos on Youtube

RKA
Author by

RKA

Updated on September 18, 2022

Comments

  • RKA
    RKA over 1 year

    Elsewhere I have seen a cd function as below:

    cd()
    {
     builtin cd "$@"
    }
    

    why is it recommended to use $@ instead of $1?

    I created a test directory "r st" and called the script containing this function and it worked either way

    $ . cdtest.sh "r st"
    

    but $ . cdtest.sh r st failed whether I used "$@" or "$1"

    • Sampo Sarrala - codidact.org
      Sampo Sarrala - codidact.org over 6 years
      cd "$*" also wont work correctly with more than 1 arg.
  • gokhan acar
    gokhan acar over 6 years
    Somewhat unrelated, but: I finally understand why a null argument to cd is a good thing. It causes an error instead of changing to the home directory. With some other commands, notably rsync, a null first argument causes unexpected behavior (includes the current directory in the sources of the transfer).