Live data from Hacker News

How does `cd` work?

blog.safia.rocks

21–30 of 93 posts

Re: How does `cd` work?

#21
Not to be negative, but for learning: There are a few "problems" with this snippet of the article:

----

    $ which cd
    /usr/bin/cd
    $ cat /usr/bin/cd
    #!/bin/sh
    # $FreeBSD: src/usr.bin/alias/generic.sh,v 1.2 2005/10/24 22:32:19 cperciva Exp $
    # This file is in the public domain.
    builtin `echo ${0##*/} | tr \[:upper:] \[:lower:]` ${1+"$@"}
Oh, bother! Reading shell scripts can be such a hassle sometimes. I know the tr command is used to translate characters. In this particular case, the second half of the command, the part after the pipe symbol, basically converts the command cd dev to CD dev. I have no idea why this is. In any case, this modified command is passed to the builtin command which is handled by the shell (Bourne shell) that we are using.

----

1. The first mistake is typing `which cd`. `which` is a separate program that looks things up in $PATH, which may not actually be what happens when you run the command. You should have used `type cd`:

    $ type cd
    cd is a shell builtin
As you discover later in the article, `cd` must be a shell builtin. Which makes it a little mysterious (and interesting!) why the file /usr/bin/cd exists; it won't really do anything, try it:

    $ pwd
    /home/lukeshu
    $ /usr/bin/cd /usr
    $ pwd
    /home/lukeshu
    $ # but it will print error messages
    $ /usr/bin/cd /bogus
    /usr/bin/cd: line 4: cd: /bogus: No such file or directory
So, why does /usr/bin/cd exist? The comment with the CVS ID gives us a hint: It's a common "src/usr.bin/alias/generic.sh" that is copied (hard-linked) in to /usr/bin for several shell builtins ( https://github.com/freebsd/freebsd/blob/0bc1bed704cc7b7292be... ). For other builtins that don't need to be builtins, it makes sense; let other programs call them with exec. For `cd` it doesn't make much sense though, and I'm not sure why it exists. Is it just for consistency with other builtins, or does it serve a real purpose? IDK.

(edit: the short answer is "POSIX says so" https://github.com/freebsd/freebsd/commit/55d0b8395514ae4055... , but why does POSIX say so? See my child comment for further citation.)

2. The second mistake is about what `tr` is doing. You claimed it's converting lowercase to uppercase; but that's backward, it's converting uppercase to lowercase.

So, why does it convert to lowercase? Recall that we learned that it's the same script being used for all builtins. If it weren't literally the same file (at the cost of a few more bytes disk space), it could have just done a search/replace within a template, having each be `builtin BUILTIN_NAME ${1+"$@"}`. But they wanted to save a few bytes, and instead the script must detect the appropriate builtin name by translating its program path to a builtin name. If you execvp("cd", ...), it will invoke the script with $0 set to "/usr/bin/cd". If /usr/bin is on a case-insensitive filesystem, and you execvp("CD", ...), that will also call the script, with $0 set to "/usr/bin/CD". How is it going to translate from "/usr/bin/CD" to "cd"? The ##*/ bit trims the leading directories, then the tr bit converts the remainder to lower case.

(as an aside: the `${1+"$@"}` is a little interesting too; why not just write `"$@"`? "$@" will expand to the full list of arguments (after argv[0]). The ${1+...} bit says to only do that expansion if the first argument exists (i.e., there are >= 1 arguments). But that should basically be happening anyway; if there are no arguments, "$@" should expand to a zero-length list. IDK, perhaps a weird historical shell?)

Re: How does `cd` work?

#23
post #11

Instead of which(1), you could use type, which is aware of builtins and aliases: $ type cd cd is a shell builtin

command is also useful for this sort of thing (with the -v and -V options). It can also be used to ignore shell functions and aliases (without options) so:

  $ alias cd=ls
  $ cd foo
   
  $ command cd foo
   

Re: How does `cd` work?

#24

Random piece of info about cd: cd.. (no space between "cd" and "..") works on Windows

It's been like that since the DOS days, IIRC. You can simulate that behavior on bash with an alias such as this one: alias cd..="cd .."

My base bashrc has this and cd-, just to handle typos

Re: How does `cd` work?

#25
post #12

Earlier quoted context omitted.

cd is part of your shell, so trying to invoke cd from another program would be like opening a new browser tab from another program. It's an internal feature without an outward-facing API.

There's no reason why one shouldn't be able to invoke the chdir system call and implement the same behaviour.

Well, I mean, a program can call chdir() and it works exactly as it's supposed to, but it only affects that program, not a different program.

Re: How does `cd` work?

#27
post #4

Here's cd implemented as a stand-alone program: https://github.com/robertswiecki/extcd

This looks fiendishly clever, if it really works, but I don't have time to dig through the 'ptrace' man page to figure out what it's doing. Can someone summarize?

Let me give it a shot: ptrace(2) allows processes to control other processes for debugging (for example, GDB and LLDB use it). What it's doing is gaining privileges to debug your shell process, using this privilege to gain control over its memory, and then just copying over the directory string to the right spot so that the shell thinks it has a new working directory.

Re: How does `cd` work?

#28
post #21

Not to be negative, but for learning: There are a few "problems" with this snippet of the article: ---- $ which cd /usr/bin/cd $ cat /usr/bin/cd #!/bin/sh # $FreeBSD: src/usr.bin/alias/generic.sh,v 1.2 2005/10/24 22:32:19 cperciva Exp $ # This file is in the public domain. builtin `echo ${0##*/} | tr \[:upper:] \[:lower:]` ${1+"$@"} Oh, bother! Reading shell scripts can be such a hassle sometimes. I know the tr comma…

Also some shells will refuse to run a builtin if there is no executable in the path that matches it.

POSIX actually mandates this behavior for any builtin not on a specific list, though most shells (even dash, which is typically obsessive about complying with POSIX) do not implement this behavior.

Re: How does `cd` work?

#29
Unrelated: it seems like this site has some sort of "debug" script set up that downloads half a dozen copies of some test image ("r20-100KB.png"). Safia, if you're on here, you might want to get it checked out since it's more than doubling the amount of time it takes your site to load.
Post reply on HN