在Unix类型的操作系统(包括Linux、MacOS)中,cp、mv和rm是用于复制、移动和删除文件和目录的标准命令。-r、*和尾部斜线(文件路径后面是否带/)都在这些命令的操作中起到独特的作用,一旦用错了,就会导致各种问题。
本文尝试逐一解析这些命令选项的意义,以及一些经常容易出问题的用法。这些知识,平常或多或少有个大概的印象,这次做一个总结。
-r:递归选项对于cp:此选项允许递归地复制目录及其内容。如果尝试在没有-r的情况下复制目录,它会报错。所以cp在涉及目录时是一定需要-r的。
# 文件结构:
src/
├── file1.txt
├── file2.txt
# 执行命令:
cp -r src/ dest/
# 结果文件结构:
src/
├── file1.txt
├── file2.txt
dest/
├── file1.txt
├── file2.txt
对于mv:不需要-r选项,因为mv可以在没有它的情况下移动目录。
# 文件结构:
src/
├── file1.txt
# 执行命令:
mv src/file1.txt dest/
# 结果文件结构:
dest/
├── file1.txt
对于rm:使用-r选项可以递归地删除目录及其内容。在没有-r的情况下使用rm删除目录会报错。
*:通配符rm *.txt 会被shell扩展成rm a.txt b.txt c.txt ...,所以rm等命令本身并不支持*选项。
尾部斜线 (/):指定目录# 文件结构:
src/
├── file1.txt
# 执行命令:
cp -r src/ dest/
# 结果文件结构:
src/
├── file1.txt
dest/
├── file1.txt
# 文件结构:
src/
├── file1.txt
# 执行命令:(注意 src 后面没有 / )
cp -r src dest/
# 结果文件结构:
src/
├── file1.txt
dest/
├──src
├── file1.txt
注意,这一行为并不稳定,我在有些操作系统(比如MacOS)上发现了这一行为,但是某些操作系统(比如Ubuntu)对源目录是否以/结尾并不敏感。
常见情况分析跨机器移动一种常见的情况是我们想用scp(类似cp)进行跨机器移动,把src目录挪到dest目录下,对应的命令是:
scp -r src server:dest
但是如果我们一不小心在src参数后面加了一个/,那情况就完全不一样了:
scp -r src/ server:dest
它并不会把src目录本身复制过去,只会把里面的内容复制过去。于是dest底下就多了一大堆文件。这种情况,就像是解压了一个压缩包但是没有新建一个文件夹存放解压文件、于是当前目录充满了解压后的文件,还无法撤销,不知道要删掉哪些文件……
(同样的,这个现象并不稳定,不同操作系统的实现不一样……)
建议不要使用下面这个命令,它可能漏掉一些*无法匹配的文件/文件夹,比如.git。
cp -r src/* dest
移动文件夹同时改名
如果想要把src目录挪到dest目录下,并重新命名为name,那么应该是mv src dest/name。注意,当src是一个文件时,不能使用mv src dest/name/,因为此时真正的完整路径其实是dest/name/src,而这个路径对应的目录并不存在。
总结使用rm/mv/cp/scp等命令的时候,涉及到-r选项、*通配符、路径是否以/结尾、目标路径是已有路径还是暂时不存在而需要创建的路径等等复杂情况。针对这些边界情况,有些操作系统的行为甚至可能不一致。
为了避免陷入类似的困惑,我们可以采用最保险的做法:
就像解压一个压缩包时,我总是先新建一个临时文件夹,把压缩包放进去解压,然后再根据压缩包本身是否带目录结构来决定接下来如何移动这些文件,避免解压出来的文件污染了当前目录。
还有一些更复杂的情况,比如使用s3等对象存储时,也涉及到路径是否以/结尾、目标路径是已有路径还是暂时不存在而需要创建的路径等等复杂情况。这个就得看对象存储服务的提供商自定义工具的支持了。
对于这些容易出错的条件,我的建议是不要去学习这么多细节,而是尽量避免陷入到这些细节中,只使用语义最明确的用法,保持我们写出来的代码的“兼容性”与“可维护性”。
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved