Update: I have found an interesting forum discussion on the double slash behavior. Older *nix systems used a // path and a /../ path prefix to mean a network filesystem. A hostname would be added, like //victim/etc/shadow. POSIX leaves the double slash to be handled in an implementation-specific fashion. After hearing this, the people on the forum start complaining about how bash is the only shell that does this and that bash is free to do it any way it wants and therefore sucks. This just isn’t true, because bash doesn’t own the filesystem. Bash doesn’t understand the filesystem. Bash just makes system calls and the Linux VFS actually figures out what these slashes mean. Linux’s specific implementation is to ignore it. If bash were to remove the extra leading slash, it would be doing the kernel’s job of interpreting paths. This is why cool things like FUSE and NFS work: Every single little program doesn’t try to understand what the filesystem is. It just takes the path, asks the kernel, and the kernel replies with a file descriptor that looks like a directory or file or pipe or socket or whatever it wants. The kernel has the power to do anything when asked for the contents of a path. It handles finding and distributing the files, and userland just uses it and pretends to like it.
How many times have you done this?
/home/jake$ cd /var/www/html/
/var/www/html$ # Have you ever been trying to work on something?
/var/www/html$ cd music
/var/www/html/music$ ls
album.py* albumart/ artist.py~* index.py~*
album.py~* artist.py* index.py* track.py*
/var/www/html/music$ ls -a
./ .svn/ album.py~* artist.py* index.py* track.py*
../ album.py* albumart/ artist.py~* index.py~*
/var/www/html/music$ cd album
-bash: cd: album: No such file or directory
/var/www/html/music$ cd albumart/
/var/www/html/music/albumart$ ls
10_12.jpg 15_34.jpg 1_25.jpg 25_61.jpg 27_76.jpg 34_84.jpg 42_97.jpg
11_13.jpg 16_35.jpg 1_26.jpg 26_62.jpg 28_77.jpg 34_85.jpg 42_98.jpg
11_14.jpg 16_36.jpg 20_48.jpg 27_63.jpg 29_78.jpg 35_86.jpg 42_99.jpg
11_15.jpg 16_37.jpg 20_49.jpg 27_64.jpg 2_27.jpg 35_87.jpg 43_101.jpg
11_16.jpg 17_38.jpg 20_50.jpg 27_65.jpg 2_28.jpg 36_88.jpg 43_102.jpg
11_19.jpg 17_39.jpg 20_51.jpg 27_66.jpg 2_4.jpg 36_89.jpg 4_6.jpg
11_20.jpg 18_40.jpg 21_52.jpg 27_67.jpg 2_42.jpg 36_90.jpg 5_7.jpg
11_21.jpg 18_41.jpg 21_53.jpg 27_68.jpg 2_43.jpg 36_91.jpg 6_8.jpg
12_17.jpg 19_46.jpg 21_54.jpg 27_69.jpg 2_44.jpg 37_92.jpg 7_9.jpg
12_18.jpg 19_47.jpg 22_55.jpg 27_70.jpg 2_45.jpg 38_93.jpg 8_10.jpg
13_29.jpg 1_1.jpg 22_56.jpg 27_71.jpg 30_79.jpg 38_94.jpg 9_11.jpg
13_30.jpg 1_2.jpg 23_57.jpg 27_72.jpg 31_80.jpg 39_95.jpg blank.png
14_31.jpg 1_22.jpg 23_58.jpg 27_73.jpg 31_81.jpg 3_5.jpg
15_32.jpg 1_23.jpg 24_59.jpg 27_74.jpg 32_82.jpg 41_96.jpg
15_33.jpg 1_24.jpg 25_60.jpg 27_75.jpg 33_83.jpg 42_100.jpg
/var/www/html/music/albumart$ # Then completely screwed up?
/var/www/html/music/albumart$ cd //
//$ pwd
//
//$ # Erm, what? "//"?
//$ ls
bin/ dev/ home/ media/ opt/ root/ srv/ sys/ usr/
boot/ etc/ lib/ music/ proc/ sbin/ svn/ tmp/ var/
//$ # Sucks, doesn't it?
It happens to me more often than I can count. It’s incredibly annoying and even dangerous if you’re mucking about in sensitive places on your system. In most cases when this happens, you meant to type “cd ..” to go up one folder. Instead, you’re placed in some half-assed root directory.
I was pretty annoyed by this after a couple of times and wanted to fix it. First I tried something like “touch //” or “touch /\/” so that typing “cd //” would fail because it’s a file. No dice, it thinks I’m trying to touch the root directory. So I tried an alias; I set up cd to alias to a script which detects the “//” and changes it to “..” and then executed the chdir(). This didn’t work because the script is launched in a subshell and so it’s cwd is independent from my shell’s. I tried again: alias cd=”cd $(mycd)” where mycd echo’s the appropriate path. This won’t work because afaik there’s no way to pass the argument from the outer shell to the inner.
Needless to say, this greatly frustrated me. In desperation and anger I grabbed The Source and started poking around. After looking at things and a few dumb segfaults, I figured out a (hacky) workaround that’s “good enough for me.”
It’s a patch to builtins/cd.def in the bash 4.1 tarball. It’s only six lines including whitespace, is a dirty hack and probably won’t be making it into the main tree anytime soon, but it solved a major headache for myself, so hopefully someone can benefit from it:
--- bash-4.1/builtins/cd.def 2009-01-04 11:32:22.000000000 -0800
+++ cd.def 2010-05-22 12:53:30.000000000 -0700
@@ -205,6 +205,12 @@
lflag = (cdable_vars ? LCD_DOVARS : 0) |
((interactive && cdspelling) ? LCD_DOSPELL : 0);
+ if (list != 0 && list->word->word[0] == '/' && list->word->word[1] == '/' && list->word->word[2] == '\0')
+ {
+ list->word->word[0] = '.';
+ list->word->word[1] = '.';
+ }
+
if (list == 0)
{
/* `cd' without arguments is equivalent to `cd $HOME' */
I’m not sure why “//” is considered a valid path. All other variations on /+ take you to the root, with just one slash. “//” moves you to something called “//”, however. Very odd how that one name is somehow different from the others. There could be a valid semantic reason for this behavior I’m unaware of that something depends on, so I wouldn’t use this as my system-wide shell before looking into it, just a login shell.