Why can't I pipe into cd?

Why can't I pipe stuff into cd?

Example:

$ 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.


Solution 1:

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.