日常中可能经常会使用到与同步或文件复制有关的命令,针对不同的场合分为很多种情况,比如复制文件到文件夹、复制文件夹到文件夹、复制文件夹里的内容到文件夹等。如果是文件夹的话可能需要考虑需不需要加斜杠,例如名为 dest 的文件夹是写成dest/
还是dest
好。而实际上不同的工具或软件对于加不加斜杠的处理大有不同。
以如下的场景为例:
1
2
3
4
5
6
|
.
├── dest_folder
│ └── dest_content.txt
└── src_folder
├── src_content1.txt
└── src_content2.txt
|
注意下文的每一次运行命令,默认都是在如上的文件结构上运行的。
对于如同command /path/a /path/b
的命令来说,我们把/path/a
称作前一个路径,/path/b
称为后一个路径。
Rclone 的场合
Rclone 应该来说各种场合是一致性最强的了。一句话总结就是:斜杠无关紧要。第一个路径可以是文件或文件夹,后一个路径只能是文件夹。
文件夹->文件夹
前后两个目录都是文件夹时,不管有没有斜杠,Rclone 的语义始终是「把前面那个文件夹底下的所有文件都复制到后一个文件夹底下」。
运行命令:
1
|
rclone copy src_folder dest_folder
|
结果:
1
2
3
4
5
6
7
8
|
.
├── dest_folder
│ ├── dest_content.txt
│ ├── src_content1.txt
│ └── src_content2.txt
└── src_folder
├── src_content1.txt
└── src_content2.txt
|
可以看到把 src_folder 里的文件都复制到 dest_folder 下面了,而非形成类似 dest_folder/src_folder 的结构。
文件->文件夹
运行命令:
1
|
rclone copy src_folder/src_content1.txt dest_folder/a.txt
|
结果:
1
2
3
4
5
6
7
8
|
.
├── dest_folder
│ ├── a.txt
│ │ └── src_content1.txt
│ └── dest_content.txt
└── src_folder
├── src_content1.txt
└── src_content2.txt
|
可以看到并不会把 src_content1.txt 复制到 dest_folder 底下改名成 a.txt,而是把后一个路径始终看成是文件夹,因为不存在而新建了一个名为 a.txt 的文件夹。
文件->文件
想使用 Rclone 把一个文件复制到另一个文件夹底下并改名,这点是做不到的。原因上面已经说了,Rclone 后一个路径只能是文件夹。
rsync 的场合
rsync 的斜杠之和前一个路径参数(原)有关,后一个路径参数(目的地)加不加斜杆都无所谓。
文件夹->文件夹
前一个路径不加斜杠
运行命令:
1
|
rsync -av src_folder dest_folder
|
结果:
1
2
3
4
5
6
7
8
9
|
.
├── dest_folder
│ ├── dest_content.txt
│ └── src_folder
│ ├── src_content1.txt
│ └── src_content2.txt
└── src_folder
├── src_content1.txt
└── src_content2.txt
|
可以看到前一个路径没加斜杠,就把整个 src_folder 连文件夹带文件都拷贝到 dest_folder 底下了。
运行命令:
1
|
rsync -av src_folder dest_folder/new_folder
|
结果:
1
2
3
4
5
6
7
8
9
10
|
.
├── dest_folder
│ ├── dest_content.txt
│ └── new_folder
│ └── src_folder
│ ├── src_content1.txt
│ └── src_content2.txt
└── src_folder
├── src_content1.txt
└── src_content2.txt
|
还是连文件夹带文件拷贝,不过会自动新建后一个路径不存在的 new_folder 文件夹。
前一个路径加斜杠
运行命令:
1
|
rsync -av src_folder/ dest_folder
|
结果:
1
2
3
4
5
6
7
8
|
.
├── dest_folder
│ ├── dest_content.txt
│ ├── src_content1.txt
│ └── src_content2.txt
└── src_folder
├── src_content1.txt
└── src_content2.txt
|
前一个路径是文件夹且后面加斜杠,意思就是把该文件夹下的所有文件拷贝到后一个目录下,不带文件夹本身。
运行命令:
1
|
rsync -av src_folder/ dest_folder/new_folder
|
结果:
1
2
3
4
5
6
7
8
9
|
.
├── dest_folder
│ ├── dest_content.txt
│ └── new_folder
│ ├── src_content1.txt
│ └── src_content2.txt
└── src_folder
├── src_content1.txt
└── src_content2.txt
|
这次是拷贝 src_folder 下的所有文件,后一个路径中的 new_folder 由于不存在所以会自动新建。
文件->文件夹 / 文件->文件
如果前一个路径是文件的话,这时候对于后一个路径来说要分几种情况。
为了方便说明我们把诸如 aa/bb/cc 的目录看作以/
隔开的 seg,其中 aa 是一个 seg、bb 是一个 seg、cc 是一个 seg。因此对于这个路径来说,cc 就是它的最后一个 seg。下面分几种情况:
后一个路径的最后一个 seg 不存在
运行命令:
1
|
rsync -av src_folder/src_content1.txt dest_folder/aaa.txt
|
结果:
1
2
3
4
5
6
7
|
.
├── dest_folder
│ ├── aaa.txt
│ └── dest_content.txt
└── src_folder
├── src_content1.txt
└── src_content2.txt
|
src _content1.txt 的内容被写到了 aaa.txt 中。
后一个路径的最后一个 seg 存在
后一个路径是文件夹
运行命令:
1
|
rsync -av src_folder/src_content1.txt dest_folder
|
结果:
1
2
3
4
5
6
7
|
.
├── dest_folder
│ ├── dest_content.txt
│ └── src_content1.txt
└── src_folder
├── src_content1.txt
└── src_content2.txt
|
src_content.txt 文件被复制到了 dest_folder 中。
后一个路径是文件
运行命令:
1
2
|
echo src_content1_content > src_folder/src_content1.txt
rsync -av src_folder/src_content1.txt dest_folder/dest_content.txt
|
结果:
1
2
3
4
5
6
7
8
9
10
11
12
|
root@dev ~/test# tree
.
├── dest_folder
│ └── dest_content.txt
└── src_folder
├── src_content1.txt
└── src_content2.txt
2 directories, 3 files
root@dev ~/test# cat dest_folder/dest_content.txt
src_content1_content
|
可以看到 dest_content.txt 的文件内容被 src_content1.txt 的内容覆盖。
后一个路径的超过一个 seg 不存在
如下所示,会报错:
1
2
3
4
|
root@dev ~/test# rsync -av src_folder/src_content1.txt dest_folder/aa/bb
sending incremental file list
rsync: [Receiver] change_dir#3 "/root/test/dest_folder/aa" failed: No such file or directory (2)
rsync error: errors selecting input/output files, dirs (code 3) at main.c(829) [Receiver=3.2.7]
|
Docker 的场合
Docker 的情况是,使用 COPY 或 ADD 命令时,前一个路径只看是文件还是文件夹,是否有斜杠无关紧要。后一个路径可以是文件或文件夹,通过是否有斜杠来判断。
如果 Docker 中后一个路径的文件夹不存在,不管有几层都会自动新建。
我们这次用以下的目录结构做测试:
1
2
3
4
5
6
|
.
└── src_folder
├── src_content1.txt
├── src_content2.txt
└── sub_folder
└── new.txt
|
文件夹->文件夹
运行命令:
1
|
COPY src_folder /test/dest_folder
|
结果:
1
2
3
4
5
6
|
/test
`-- dest_folder
|-- src_content1.txt
|-- src_content2.txt
`-- sub_folder
`-- new.txt
|
不管前面加不加斜杠,都是把 src_folder 目录下的所有内容复制到/test/dest_folder 底下去,不包括 src_folder 本身。那如果一定要保留 src_folder 怎么办?只能自己在后面加一个文件夹名了,比如:
1
|
COPY src_folder /test/dest_folder/src_folder
|
文件->文件夹 / 文件->文件
如果前一个目录是文件,需要分两种情况来讨论。
后一个路径加斜杠(文件->文件夹)
运行命令:
1
|
COPY src_folder/src_content1.txt /test/dest_folder/
|
结果:
1
2
3
|
/test
`-- dest_folder
`-- src_content1.txt
|
可以看到如果后一个路径加了斜杆的话,会把它当做文件夹看待,而把前一个路径的文件复制到指定的文件夹下。
后一个路径不加斜杠(文件->文件)
运行命令:
1
|
COPY src_folder/src_content1.txt /test/dest_folder
|
结果:
1
2
|
/test
`-- dest_folder
|
这里出现了非预期行为。实际上由于后一个路径没有加斜杠,Docker 把后一个路径当做了文件,而把 src_content1.txt 的内容拷贝了过来。这里 tree 命令显示的 dest_folder 其实是一个文件,不是文件夹,其文件内容是 src_content1.txt 的内容。
这种情况其实还能同时复制多个文件,比如官方文档中的例子:
1
|
COPY file1.txt file2.txt /usr/src/things/
|
会把这两个文件复制到 things 文件夹下。同时,前一个路径是通配符也是可以的,比如file*.txt
。
参考:https://docs.docker.com/reference/dockerfile/#copy