0

I have the following script:

#!/bin/sh

mkdir -p /tmp/test/foo

cd /tmp/test

unset CDPATH
echo "TEST1: Current working directory: ${PWD}"
echo "TEST1: attempting to 'cd foo'"
cd foo
echo "TEST1: Current working directory: ${PWD}"

cd /tmp/test
CDPATH=/tmp
echo "TEST2: Current working directory: ${PWD}"
echo "TEST2: attempting to 'cd foo'"
cd foo
echo "TEST2: Current working directory: ${PWD}"

Which prints out:

$ ./cdpath.sh
TEST1: Current working directory: /tmp/test
TEST1: attempting to 'cd foo'
TEST1: Current working directory: /tmp/test/foo
TEST2: Current working directory: /tmp/test
TEST2: attempting to 'cd foo'
./cdpath.sh: line 17: cd: foo: No such file or directory
TEST2: Current working directory: /tmp/test

However, if I run it as /bin/bash cdpath.sh there is no error, and the TEST2 output is identical to TEST1. The error only occurs if I run it with /bin/sh. I haven't found any documentation about bourne and bourne again shells interpreting CDPATH differently:

http://tldp.org/LDP/abs/html/internalvariables.html#CDPATHREF http://tldp.org/LDP/abs/html/internal.html#CDREF

  • Perhaps BASH always has an implied . in the CDPATH, while Bourne Shell doesn't? – Gabe Jan 27 '14 at 19:29
  • What platform are you on? In ubuntu /bin/sh is linked to dash. – Elliott Frisch Jan 27 '14 at 19:32
  • Hmm, I just found this: bash.cyberciti.biz/bash-reference-manual/Bash-POSIX-Mode.html (it's #19) by searching google for [bash sh posix cdpath]. Strangely I couldn't find any official documentation that described this difference, and I thought the bash people were pretty good about documenting when posix behavior differed, just third-party sites. I think people need to either not export CDPATH, so subshells that run as /bin/sh don't use it, and/or use CDPATH=:/foo/bar, the empty initial string means the current directory. – onlynone Jan 27 '14 at 19:40
  • I'm guessing you'll want to change your script... Use ./foo explicitly when changing directories relative to the current directory. – user45405 Jan 27 '14 at 20:09
  • See my full answer below, but the tldr is: bash's cd in non-posix mode has always been the same, the posix standard for cd changed between POSIX.2-1992 and POSIX.1-2008, bash implemented the change for posix cd between versions 4.1 and 4.2, the current online docs reflect the current behavior without any reference to the fact that the behavior changed, I'm using bash 3.2. – onlynone Jan 27 '14 at 21:20
4

After much more googling and source code checking I found out the reason I'm seeing this behavior is that I'm running bash 3.2. In this version of bash, if you were running in posix mode (which happens when you run bash through /bin/sh) and CDPATH is set, then cd won't search the currently directory unless it's explicitly set in CDPATH. This behavior was included to conform to POSIX.2 (IEEE 1003.2-1992). This reflects what their manual specified at the time. However, for bash 4.2 they changed the behavior in posix mode to match the updated POSIX.1-2008 which now specifies that cd should always search the current working directory even if CDPATH doesn't include it. Bash's new manual now no longer includes this as a difference between posix and non-posix mode because it no longer is different.

They made the change by disabling the code that raises an error if posix mode is on by surrounding it with an #if 0 block in builtins/cd.def:

#if 0
      /* changed for bash-4.2 Posix cd description steps 5-6 */
      /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
         try the current directory, so we just punt now with an error
         message if POSIXLY_CORRECT is non-zero.  The check for cdpath[0]
         is so we don't mistakenly treat a CDPATH value of "" as not
         specifying the current directory. */
      if (posixly_correct && cdpath[0])
        {
          builtin_error ("%s: %s", dirname, strerror (ENOENT));
          return (EXECUTION_FAILURE);
        }
#endif

I wish the current bash docs noted the fact that older versions of bash had this behavior.

2

The original Bourne shell (/bin/sh) did not have CDPATH. That was one of the innovations of the Kornshell which was later added to the POSIX standard and then to bash itself. How it's implemented my differ from shell to shell and the mode that shell is in.

On many systems, /bin/sh is a hard link to /bin/bash, but bash changes its behavior when invoked as sh instead of bash. From the bash manpage:

If bash is invoked with the name sh, it tries to mimic the startup behavior of historical versions of sh as closely as possible, while conforming to the POSIX standard as well. When invoked as an interactive login shell, or a non-interactive shell with the --login option, it first attempts to read and execute commands from /etc/profile and ~/.profile, in that order.

On some systems, /bin/sh is actually the dash` shell which descended from the ash shell.

So, the difference you're seeing may be due to the way bash works when being bash, bash works when it's operating in POSIX compatibility mode, or the fact you're using dash or ash instead of bash which may or may not implement CDPATH in it's own unique way.

When using a POSIX compatible shell, the . directory is not automatically included in CDPATH. However, if a shell isn't completely POSIX compliant, it may automatically include . in CDPATH for you.

I found this bit of info on Softpanorama:

Warning: Watch out when running bash in POSIX mode (e.g., as /bin/sh or with --posix). As the bash Reference notes:"If $CDPATH is set, the cd built-in will not implicitly append the current directory to it. This means that cd will fail if no valid directory name can be constructed from any of the entries in $CDPATH, even if a directory with the same name as the name given as an argument to cd exists in the current directory.

"To avoid this, explicitly include . in $CDPATH. However, if you do that, then another subtle point noted in the bash Reference comes into play:"If a nonempty directory name from $CDPATH is used, or if '-' is the first argument, and the directory change is successful, the absolute pathname of the new working directory is written to the standard output."In other words, pretty much every time you use cd it will echo the new path to STDOUT, which is not the standard behavior.

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.