Why can't I pipe stuff into cd?


$ pwd >> ~/mloc
$ cd /
$ tail -n 1 ~/mloc | cd

cd fails. If do: cd $(tail -n 1 ~/mloc), it works.

I understand the last command I wrote works, but I don't understand why I can't pipe into cd.


Piping to a process sends data to it in place of what you'd type after you have launched the process, not what you'd type as part of the command that launches the process.

(cd is also not a process but that's less important, I address it below.)

Piping attaches one command's output to another command's input. Consider:

foo | bar

That runs bar, runs foo, and:

  • instead of showing foo's output on the terminal, it directs it to bar as input.
  • instead of taking input to bar from the terminal, it takes it from foo's output.

(Both those bullet points actually express the same thing.)

So, what happens when you run tail -n 1 ~/mloc | cd?

  • It runs cd and, whenever cd accepts input while running, it takes that input from the output of tail -n 1 ~/mloc.
  • cd never accepts input while running.

See standard streams (Wikipedia) for more information on the precise meaning of input and output in this answer. (Here, by input, I mean "standard input" and by output I mean "standard output.")

There is also the issue that cd is not actually a program, but a shell builtin, so when you run cd no new process is launched. But that is not really why what you're trying doesn't work, because:

  • Using pipes to pass command line arguments, as you've been attempting, will not ever work for any program.

  • There's no reason a shell couldn't be written to accommodate pipes to a shell builtin, considering that they accommodate pipes from shell builtins.

    Note that sometimes when you use a shell builtin like it's a real program, what happens is that the real program of the same name is run instead. But there's no separate program for cd, and we can pipe from cd; for example, run cd blah 2>&1 | less; if blah doesn't exist or isn't a directory, you'll be able to view the error in less.

Finally, consider what's different about cd $(tail -n 1 ~/mloc). This command:

  • Runs tail -n 1 ~/mloc and captures its output.
  • Instead of displaying the output, constructs a command consisting of it appended to cd .
  • Runs that command.

The short answer is: cd doesn't work like that.


The longer answer is that in bash, commands in a pipeline are each executed in a subshell. Since cd is a shell builtin, it only affects the shell it's executed in. If you cd within a subshell, the effect will disappear when the subshell exits.

  • This is not really why. The OP is trying to use a pipe to construct an argument string; this never works, it's not specific to cd. Jan 12 '14 at 1:28

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.