The most recent POSIX specification for the cd
utility (Issue 8, from 2024) has this in the OPERANDS section:
[...] If directory is an empty string, cd
shall write a diagnostic message to standard error and exit with non-zero status. [...]
However, at the time of writing (2025), this is a fairly recent addition, and the previous issue of the standard said that it was "unspecified" what an empty operand would cause the utility to do, leaving it up to the implementors of the standard to provide a useful consistent behaviour.
All shells I use daily (bash, zsh, OpenBSD ksh, ash) treat cd ''
as cd .
(or the first reachable directory in $CDPATH
if set), while the (very much non-POSIX) rc
(and derivatives) and fish
shell generate an error for cd ''
.
The cd
of csh
/tcsh
reports an error unless $cdpath
in set in which case like on those shells mentioned above for $CDPATH
, it chdir()
s to the first reachable directory in that array.
If these shells aspire to be POSIX compliant (some do, others don't, but tend to align the general behaviour of built-in standard utilities to the common standard), I'm sure they will be updated over time to conform to the new specification.
In fact, the most recent development version of bash does this:
bash-5.3$ mydir=
bash-5.3$ cd "$mydir"
bash: cd: null directory
bash-5.3$ echo "$?"
1
bash-5.3$ echo "$BASH_VERSION"
5.3.0(1)-rc1
In the meantime, if this is important to your own scripts, you should test the value of the shell variable used as the operand to cd
. A minimal thing to do could be to use something like cd "${mydir:?Empty or unset variable}"
in your script:
$ mydir=
$ cd "${mydir:?Empty or unset variable}"
bash: mydir: Empty or unset variable
$ echo "$?"
1
... or do a separate sanity check on the variable before its use.
bash
) only withcd -L
(the default) but not withcd -P
.cd -P
all the time.bash
, the solution is "always usecd -P
". Just another gotcha to file away./tmp
, runningcd foo
in Bash 5.2 callschdir("/tmp/foo")
, i.e. it determines the full path itself; while withcd -P foo
, it just callschdir("foo")
and lets the OS resolve it. (it does try toreadlink()
the path components first, though.) Probably has something to do with how it handlescd ..
, removing one path component fromPWD
, regardless of if it was a symlink and the filesystem..
would actually be elsewhere. Socd ""
might just append the empty string to the current directory and then try to resolve any..
s. Ending up with just the current dir.