29

I am trying to get cd to accept a directory name redirected to it from another command. Neither of these methods work:

$ echo $HOME | cd
$ echo $HOME | xargs cd

This does work:

$ cd $(echo $HOME)

Why does the first set of commands not work, and are there others that also fail this way?

4
  • By others are you referring to other commands or other methods to use cd that fail this way?
    – Didi Kohen
    Oct 5, 2012 at 15:46
  • @DavidKohen I refer to other commands
    – Jhonathan
    Oct 5, 2012 at 16:49
  • Some notable examples are ulimit, umask, popd, pushd, set, export and read.
    – Didi Kohen
    Oct 7, 2012 at 8:10
  • Why is cd not a program?
    – phuclv
    Jun 26, 2018 at 2:27

6 Answers 6

35

cd is not an external command - it is a shell builtin function. It runs in the context of the current shell, and not, as external commands do, in a fork/exec'd context as a separate process.

Your third example works, because the shell expands the variable and the command substitution before calling the cd builtin, so that cd receives the value of ${HOME} as its argument.

POSIX systems do have a binary cd - on my FreeBSD machine, it's at /usr/bin/cd, but it doesn't do what you think. Calling the binary cd causes the shell to fork/exec the binary, which does indeed change its working directory to the name you pass. However, as soon as it does so, the binary exits, and the forked/exec'd process disappears, returning you to your shell, which is still in the directory it was in before you started.

1
23

cd does not read standard input. That is why your first example does not work.

xargs needs a command name, that is, a name of an independant executable. cd needs to be a shell built-in command and would have no effect (other than verifying that you can change to that directory and the potential side effects it may have like for automountable directories) if it were an executable. That is why your second example does not work.

4

In addition to the existing good answer, it is also worth mentioning that a pipe forks a new process, which has its own separate working directory. Therefore, trying to do this, won't work:

echo test | cd /

Thus, you won't be in the / folder after the shell returns from this command.

1
  • All the commands in a pipeline run in different processes, so in a | b, even if a and b are builtin, at least one then doesn't run in the shell process, but there's no guarantee which which one it is. For instance in AT&T ksh, zsh or bash -O lastpipe, b is run in the current shell process, so your code would take you to / there. Jun 22, 2013 at 21:01
4

In addition to the correct answers already given: If you run bash and want to find out what a "command" like cd is you can use type

$ type cd
cd is a shell builtin

or why not:

$ type time
time is a shell keyword

while for example gnu time normally is included in your favorite distribution already:

$ which time
/usr/bin/time

Okey okey you get the idea, then what the heck is type?

$ type type
type is a shell builtin

Here's a bash manual snippet:

       type [-aftpP] name [name ...]
          With no options, indicate how each name would be interpreted  if  used  as  a
          command name.  If the -t option is used, type prints a string which is one of
          alias, keyword, function, builtin,  or  file  if  name  is  an  alias,  shell
          reserved word, function, builtin, or disk file, respectively.  If the name is
          not found, then nothing is printed, and an exit status of false is  returned.
          If  the -p option is used, type either returns the name of the disk file that
          would be executed if name were specified as a command  name,  or  nothing  if
          ‘‘type  -t  name’’ would not return file.  The -P option forces a PATH search
          for each name, even if ‘‘type -t name’’ would not return file.  If a  command
          is  hashed,  -p  and -P print the hashed value, not necessarily the file that
          appears first in PATH.  If the -a option is used,  type  prints  all  of  the
          places  that  contain  an  executable  named name.  This includes aliases and
          functions, if and only if the -p option is  not  also  used.   The  table  of
          hashed  commands  is  not  consulted when using -a.  The -f option suppresses
          shell function lookup, as with the command builtin.  type returns true if any
          of the arguments are found, false if none are found.
0

As others have said, it won't work because cd is a shell builtin command, not an external program, so it doesn't have any standard input you can pipe anything into.

But, even if it worked, it wouldn't do what you wish: a pipe spawns a new process and redirects the standard output of the first command into the standard input of the second one, thus only the new process would change its current working directory; this couldn't affect the first process in any way.

1
  • 2
    Your second paragraph is right. But re the first paragraph: why do you assume that shell builtins have no stdin? read is usually (always?) a shell builtin. It is true that cd ignores stdin, but this isn't due to its being a builtin.
    – dubiousjim
    Oct 6, 2012 at 22:46
-2

Another option are backticks, which place the stdout of one command as a command line argument of a second command, and are more portable than $(...). For example:

cd `echo $HOME`

or more generally;

cd `anycommand -and whatever args`

Note that the use of backticks depends on the shell for executing the command and substituting the output on the command line. Most shells support it.

6
  • 3
    The OP already stated, in his/her question, that $(...) works. I don't think it's good advice to recommend backticks instead, since they have much more convoluted quoting rules and are generally more error-prone. (See §3.5.4 "Command Substitution" in the Bash Reference Manual.)
    – ruakh
    Oct 5, 2012 at 17:01
  • $() is nice where supported, but backticks are more widely supported across different shells and systems. But I should rephrase this as "another option".
    – Seth Noble
    Oct 5, 2012 at 17:07
  • Different shells, yes; but different "systems"? Are there really any shells that will support $(...) on one system but not on another??
    – ruakh
    Oct 5, 2012 at 17:14
  • 4
    -1, it does not answer the question at all.
    – Bernhard
    Oct 5, 2012 at 17:17
  • 3
    @ruakh &Seth: All POSIX shells support $(…). Systems without a POSIX shell (i.e. with a genuine Bourne shell) would be extremely old. Even systems where /bin/sh is a Bourne shell and you need another path such as /usr/xpg4/bin/sh to get a POSIX shell are rare nowadays. Recommending backticks to anyone who doesn't administer ancient unix boxen professionally is doing them a disservice. Oct 5, 2012 at 18:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .