rsync, scp and trailing slashes
2020-07-05
In the OpenSSH 8.0 Release Notes, the developers recommended against the use of scp
in favour of "more modern protocols". During my search for an alternative, I learned that rsync
, for some reason, handles a trailing slash on the source directory differently than both scp
and cp
, which a lot of
reddit users
found annoying.
Since this is supposedly different on BSD and Linux, I tested how all three commands behave:
#!/bin/sh cp -r source cp cp -r source/ cp-slash scp -r source scp scp -r source/ scp-slash rsync -r source rsync rsync -r source/ rsync-slash
The source directory contains three files "a", "b" and "c", the target directories all exist and are empty.
Results
BSD
Tree View
$ tree . ├── source │ ├── a │ ├── b │ └── c │ ├── cp │ └── source │ ├── a │ ├── b │ └── c ├── cp-slash │ ├── a │ ├── b │ └── c ├── scp │ └── source │ ├── a │ ├── b │ └── c ├── scp-slash │ ├── a │ ├── b │ └── c ├── rsync │ └── source │ ├── a │ ├── b │ └── c └── rsync-slash ├── a ├── b └── c
So far, all three commands behave exactly the same: If no trailing slash is used, a new subdirectory "target/source
" will be created, otherwise the content of the source directory is dumped right into into the target.
In other words: If no trailing slash is used, the directory itself is copied, otherwise its content is copied.
Linux
Tree View
$ tree . ├── source │ ├── a │ ├── b │ └── c │ ├── cp │ └── source │ ├── a │ ├── b │ └── c ├── cp-slash │ └── source │ ├── a │ ├── b │ └── c ├── scp │ └── source │ ├── a │ ├── b │ └── c ├── scp-slash │ └── source │ ├── a │ ├── b │ └── c ├── rsync │ └── source │ ├── a │ ├── b │ └── c └── rsync-slash ├── a ├── b └── c
On Linux, cp
and scp
ignore the slash, they always create a new subdirectory. However, rsync
behaves like it does on BSD. This is especially problematic if combined with shell autocompletion, which will usually add a trailing slash to directories. When this happens unexpectedly, it will definitely lead to a bunch of garbage being dumped into a folder sooner or later.
Exceptions
These patterns are nice and all, but as always, there are exceptions. At least those are the same on both BSD and Linux:
-
If the target directory does not exist,
cp
andscp
will create it and dump the source files right into it. Unfortunately,rsync
is different again, additionally creating another subdirectory if no trailing slash was appended to the source. -
If running from inside the source directory and using a dot (
.
or./
) to reference it (e.g.cp -r . target
), the files will be dumped into the target directory and no subdirectory will be created, regardless of whether there's a slash or the target directory already exists.
Trailing Slash on the target
Fortunately, a trailing slash on the target directory does not appear to make a difference, both if the target exists and if it doesn't.
Summary
rsync
usually acts like cp
and scp
do on BSD systems, unless the target directory does not exist and no trailing slash was appended to the source path. It is more consistent on BSD than on Linux, but still doesn't fit in completely.