87

Possible Duplicate:
How linux handles multiple path separators (/home////username///file)

Most commands I use in linux behave exactly the same whether I include the trailing slash / character on the end of a directory name or not.

For example:

ls /home/cklein
ls /home/cklein/

cp foo bar
cp foo/ bar/

When does this trailing slash matter? What is the semantic meaning of the trailing slash?

Cory Klein
  • 18,911
  • I think the answers here cover the subject okay—it doesn't seem direly in need of new answers—but @djsmith is correct; it's not a duplicate. The other question is related but not at all the same. – Wildcard Jan 19 '17 at 22:57
  • Honestly, this isn't a duplicate at all. This is about trailing slashes on a directory, not multiple consecutive slashes. I just wrote a good test case with rsync that I want to post here for illustration of the different cases, but I can't until the question is reopened. – Wildcard Aug 08 '17 at 22:24
  • Agreed - I added my nomination to reopen. – Cory Klein Aug 08 '17 at 22:25
  • I've reopened because https://unix.stackexchange.com/questions/870/the-slash-after-a-directory-name-on-shell-commands is a better duplicate. – Gilles 'SO- stop being evil' Aug 08 '17 at 22:35
  • 2
    Since you're still around, would you mind at least not accepting a one-liner answer that didn't even get its meager facts right? – Gilles 'SO- stop being evil' Aug 08 '17 at 22:39

4 Answers4

67

One good example is moving a file into a directory:

mv some_file foo

vs.

mv some_file foo/

If foo doesn't exist, the first will rename some_file to foo, rather than the intended foo/some_file; the second will complain, which is what you want.

If foo does exists but isn't a directory, the first can clobber the foo file; again, the second will complain.

cp presents similar issues.

Working on some old versions of SunOS, I developed the habit of appending /., because the system actually ignored a trailing / on a file name; for example, /etc/motd/ would refer to the file rather than being an error. Later versions of SunOS / Solaris don't seem to have that problem.

Josh
  • 8,449
45

It is completely dependent on the tool. rm won't let you remove a symlink to a directory if there's a slash at the end, and rsync does different things if the source file specification has a slash at the end.

  • 31
    Pay very close attention when using rsync!!! The trailing slash really matters. – Josh Oct 10 '12 at 19:48
  • 25
    For the most part, a trailing / means “treat this as a directory, dereference the symlink if it is one, complain if it isn't a directory”. This behavior is mandated for utilities specified by POSIX. Rsync's source argument is a notable exception. – Gilles 'SO- stop being evil' Oct 10 '12 at 23:42
  • 3
    Note that by default, chown -R /dir1/symlink1 will not perform recursion when the target is a symbolic link; however, chown -R /dir1/symlink1/ will do what you would expect. – chrisfargen Nov 17 '13 at 08:08
  • It does depend on the tool, but the rm case is not a good example for this: The difference here is not related to rm. In rm ./symlinkToDir, symlinkToDir is a symlink. But in rm ./symlinkToDir/, symlinkToDir/ is the same as symlinkToDir/. by definition, and that is the directory the symlink points to. The chown example @chrisfargen is very similar. – Volker Siegel Jul 24 '14 at 04:29
  • 2
    Even though the OP asked specific for Linux, it may be worth noting that the OS or flavor of tools used may make a difference. One notable example is cp which has a different behavior when using the -r option on OS X (BSD cp) and Linux (GNU cp). cp -r src/ dest will only copy the contents of src into dest when using BSD cp but will copy the direcory src itself into dest when using GNU cp. (see http://jondavidjohn.com/linux-vs-osx-the-cp-command/) – matlehmann Sep 17 '15 at 15:27
  • 3
    @mathlehmann Nice find. Devs wanting to be too clever by half again. Instead of a clear-as-day additional option ("--copy-unto" for example), let's use obscure syntax that no-one ever remembers how to use. – David Tonhofer Aug 08 '16 at 14:40
  • This answer says "rsync does different things if the remote file specification has a slash at the end," but it appears that it's really only if the source file specification has a slash that you get different behavior - right? It doesn't seem to matter whether there is or isn't a slash on the destination directory, regardless of which file (if any) is remote. Or am I missing something? (Also @Gilles.) – Wildcard Aug 08 '17 at 22:13
  • 2
    @Wildcard Yes, with rsync, a trailing / matters for source files. It doesn't matter whether the file is remote. – Gilles 'SO- stop being evil' Aug 08 '17 at 22:31
20

foo/ is like foo/., so if foo is a symlink to a directory, foo/ is a directory (not a symlink), and if foo is not a directory or a symlink to a directory, then you get a ENOTDIR error for anything trying to access foo/. That's the behavior on Linux.

Behavior may differ on other systems.

See here and here and here to see what POSIX/SUS have to say about it.

  • I've edited the first two links to POSIX specifications but I'm not fully confident about the third link - which I presume should be to http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap03.html#tag_21_03_00_75 – Anthony Geoghegan Nov 08 '16 at 16:12
  • 2
    Thanks. Yes it's annoying that the TC2 update is now available at the same URL as the previous versions but with completely different HTML anchors. Many of the links are even broken there, so I'd expect it will change again. Might be better to link to the web archive to get consistent links. – Stéphane Chazelas Nov 08 '16 at 16:44
  • "foo/ is like foo/." - It seems you haven't said they work exactly the same. Could you explain what might be the practical difference between them? – jsx97 Mar 08 '24 at 10:33
12

In rsync, the man page reads as follows:

   A trailing slash on the source changes this behavior to avoid  creating
   an  additional  directory level at the destination.  You can think of a
   trailing / on a source as meaning "copy the contents of this directory"
   as  opposed  to  "copy  the  directory  by name", but in both cases the
   attributes of the containing directory are transferred to the  contain-
   ing  directory on the destination.  In other words, each of the follow-
   ing commands copies the files in the same way, including their  setting
   of the attributes of /dest/foo:

          rsync -av /src/foo /dest
          rsync -av /src/foo/ /dest/foo

The trailing slash on the destination doesn't matter at all. Only on the source. (And then, of course, it only matters if the source is a directory rather than a single file or a glob.)

To illustrate the directory to directory use case:

$ mkdir foo bar baz
$ chmod 700 foo
$ chmod 750 bar
$ chmod 705 baz
$ echo hello > foo/file1
$ chmod 606 foo/file1 
$ ls -n
total 0
drwxr-x---  2 501  20   68 Aug  8 15:29 bar/
drwx---r-x  2 501  20   68 Aug  8 15:29 baz/
drwx------  3 501  20  102 Aug  8 15:30 foo/
$ ls -n foo
total 8
-rw----rw-  1 501  20  6 Aug  8 15:30 file1
$ rsync -a foo bar
$ rsync -a foo baz/
$ rsync -a foo bif
$ rsync -a foo bonk/
$ ls -n
total 0
drwxr-x---  3 501  20  102 Aug  8 15:30 bar/
drwx---r-x  3 501  20  102 Aug  8 15:30 baz/
drwxr-xr-x  3 501  20  102 Aug  8 15:30 bif/
drwxr-xr-x  3 501  20  102 Aug  8 15:30 bonk/
drwx------  3 501  20  102 Aug  8 15:30 foo/
$ ls -n *
bar:
total 0
drwx------  3 501  20  102 Aug  8 15:30 foo/

baz:
total 0
drwx------  3 501  20  102 Aug  8 15:30 foo/

bif:
total 0
drwx------  3 501  20  102 Aug  8 15:30 foo/

bonk:
total 0
drwx------  3 501  20  102 Aug  8 15:30 foo/

foo:
total 8
-rw----rw-  1 501  20  6 Aug  8 15:30 file1
$ rm -rf b*
$ mkdir bar baz
$ chmod 750 bar
$ chmod 705 baz
$ rsync -a foo/ bar
$ rsync -a foo/ baz/
$ rsync -a foo/ bif
$ rsync -a foo/ bonk/
$ ls -n
total 0
drwx------  3 501  20  102 Aug  8 15:30 bar/
drwx------  3 501  20  102 Aug  8 15:30 baz/
drwx------  3 501  20  102 Aug  8 15:30 bif/
drwx------  3 501  20  102 Aug  8 15:30 bonk/
drwx------  3 501  20  102 Aug  8 15:30 foo/
$ ls -n *
bar:
total 8
-rw----rw-  1 501  20  6 Aug  8 15:30 file1

baz:
total 8
-rw----rw-  1 501  20  6 Aug  8 15:30 file1

bif:
total 8
-rw----rw-  1 501  20  6 Aug  8 15:30 file1

bonk:
total 8
-rw----rw-  1 501  20  6 Aug  8 15:30 file1

foo:
total 8
-rw----rw-  1 501  20  6 Aug  8 15:30 file1
$ 
Wildcard
  • 36,499
  • 3
    +1. I use rsync all the time and have done so for many years, and I can still never remember the exact details of rsync's handling of a trailing slash on the source dir. I always have to --dry-run it first or read the man page. It's good to see this answer, but it's unlikely to fix my memory :( – cas Aug 09 '17 at 01:51