Inconsistent Behaviors among cp, rsync, and Others
Inconsistent behaviors for coping directory among cp, rsync, aws s3 cp, aws s3 sync, az storage copy and gsutil cp are sometimes confusing. Here is a list of their behaviors and examples for you to better understand them.
Assume we have the following directory structure in current directory and cloud storage before running following commands:
.
├── dir1
│ └── file1
├── dir2
│ └── file2
└── dir3
└── file3
cp
- When destination directory does NOT exist,
- the destination directory is created, and the content of source directory is copied.
- When destination directory exists,
- by default, both the directory itself and its content are copied to the destination directory;
- unless the source directory is
., or is suffixed with/., by then, only the content of source directory is copied.
Note: Suffix a directory with / is the same as not suffixing it.
Note: The behavior of cp is consistent with mv.
cp Examples
$ # Destination not exists
$ cp -r -v dir1 dir4
'dir1' -> 'dir4'
'dir1/file1' -> 'dir4/file1'
$ # Source dir suffixed with /.
$ # Destination dir not exists
$ cp -r -v dir1/. dir5
'dir1/.' -> 'dir5'
'dir1/./file1' -> 'dir5/file1'
$ # Destination dir exists
$ cp -r -v dir1 dir2
'dir1' -> 'dir2/dir1'
'dir1/file1' -> 'dir2/dir1/file1'
$ # Source dir suffixed with /.
$ # Destination dir exists
$ cp -r -v dir1/. dir3
'dir1/./file1' -> 'dir3/./file1'
rsync
- If destination directory does NOT exist, it's created;
-
- by default, both the directory itself and its content are copied to the destination directory;
- unless the source directory is
., or is suffixed with/or/., by then, only the content of source directory is copied.
Note: Suffix a directory with / is the same as suffixing it with /. .
rsync Examples
$ # Destination not exists
$ rsync -r dir1 dir4
$ tree dir4
dir4
└── dir1
└── file1
1 directory, 1 file
$ # Source dir suffixed with /.
$ # Destination not exists
$ rsync -r dir1/. dir5
$ tree dir5
dir5
└── file1
0 directories, 1 file
$ # Destination dir exists
$ rsync -r dir1 dir2
$ tree dir2
dir2
├── dir1
│ └── file1
└── file2
$ # Source dir suffixed with /.
$ # Destination dir exists
$ rsync -r dir1/. dir3
$ tree dir3
dir3
└── file1
0 directories, 1 file
aws s3 cp / aws s3 sync
It's always the content of source directory are copied to the destination directory.
Note: Suffix a directory with / is the same as not suffixing it or suffixing it with /. .
aws Examples
$ aws s3 cp --recursive dir1 s3://zzz.buzz/dir2
upload: dir1/file1 to s3://zzz.buzz/dir2/file1
$ aws s3 cp --recursive dir1/. s3://zzz.buzz/dir3
upload: dir1/file1 to s3://zzz.buzz/dir3/file1
$ aws s3 cp --recursive dir1 s3://zzz.buzz/dir4
upload: dir1/file1 to s3://zzz.buzz/dir4/file1
$ aws s3 cp --recursive dir1/. s3://zzz.buzz/dir5
upload: dir1/file1 to s3://zzz.buzz/dir5/file1
$ aws s3 cp --recursive s3://zzz.buzz/dir2 dir1
download: s3://zzz.buzz/dir2/file1 to dir1/file1
$ aws s3 sync dir1 s3://zzz.buzz/dir2
upload: dir1/file1 to s3://zzz.buzz/dir2/file1
$ aws s3 sync dir1/. s3://zzz.buzz/dir3
upload: dir1/file1 to s3://zzz.buzz/dir3/file1
$ aws s3 sync dir1 s3://zzz.buzz/dir4
upload: dir1/file1 to s3://zzz.buzz/dir4/file1
$ aws s3 sync dir1/. s3://zzz.buzz/dir5
upload: dir1/file1 to s3://zzz.buzz/dir5/file1
$ aws s3 sync s3://zzz.buzz/dir2 dir1
download: s3://zzz.buzz/dir2/file1 to dir1/file1
az storage copy
It's always the directory iteself and its content get copied to the destination directory.
Note: Suffix a directory with / is the same as not suffixing it or suffixing it with /. .
az Examples
$ az storage copy -r -s dir1 -d https://zzzbuzz.blob.core.windows.net/container/dir2
$ az storage blob list --account-name zzzbuzz -c container --prefix dir2 --query "[].{name:name}" --output tsv
dir2/dir1/file1
dir2/file2
$ az storage copy -r -s dir1/. -d https://zzzbuzz.blob.core.windows.net/container/dir3
$ az storage blob list --account-name zzzbuzz -c container --prefix dir3 --query "[].{name:name}" --output tsv
dir3/dir1/file1
dir3/file3
$ az storage copy -r -s dir1 -d https://zzzbuzz.blob.core.windows.net/container/dir4
$ az storage blob list --account-name zzzbuzz -c container --prefix dir4 --query "[].{name:name}" --output tsv
dir4/dir1/file1
$ az storage copy -r -s dir1/. -d https://zzzbuzz.blob.core.windows.net/container/dir5
$ az storage blob list --account-name zzzbuzz -c container --prefix dir5 --query "[].{name:name}" --output tsv
dir5/dir1/file1
$ az storage copy -r -s https://zzzbuzz.blob.core.windows.net/container/dir1 -d download
$ tree download
download
└── dir1
└── file1
1 directory, 1 file
gsutil cp
- When destination directory does NOT exist,
- the destination directory is created, and the content of source directory is copied.
- When destination directory exists,
- by default, both the directory itself and its content are copied to the destination directory;
- unless the source directory is
., or is suffixed with/., by then, only the content of source directory is copied.
Note: Suffix a directory with / is the same as not suffixing it.
Note: The behavior of gsutil cp is consistent with cp.
gsutil Examples
$ # Destination not exists
$ gsutil cp -r dir1 gs://zzz.buzz/dir4
$ gsutil ls -r gs://zzz.buzz
gs://zzz.buzz/dir4/:
gs://zzz.buzz/dir4/file1
$ # Source dir suffixed with /.
$ # Destination not exists
$ gsutil cp -r dir1/. gs://zzz.buzz/dir5
$ gsutil ls -r gs://zzz.buzz
gs://zzz.buzz/dir5/:
gs://zzz.buzz/dir5/file1
$ # Destination dir exists
$ gsutil cp -r dir1 gs://zzz.buzz/dir2
$ gsutil ls -r gs://zzz.buzz
gs://zzz.buzz/dir2/:
gs://zzz.buzz/dir2/file2
gs://zzz.buzz/dir2/dir1/:
gs://zzz.buzz/dir2/dir1/file1
$ # Source dir suffixed with /.
$ # Destination dir exists
$ gsutil cp -r dir1/. gs://zzz.buzz/dir3
$ gsutil ls -r gs://zzz.buzz
gs://zzz.buzz/dir3/:
gs://zzz.buzz/dir3/file1
gs://zzz.buzz/dir3/file3