复制并创建目标目录(如果不存在)
1. 概述
当我们使用 Linux 命令行时,文件复制是一种常见的文件操作。通常,我们会使用cp 命令来复制文件。
但是, 如果目标目录不存在, cp命令将不起作用。
在本教程中,我们将讨论如何在复制文件时自动创建不存在的目标目录。
2. 问题介绍
为了清楚地理解问题,让我们看一个将文件复制到不存在的目标目录的示例:
$ cp $HOME/.vimrc /tmp/test/one/non-exist/backup/dir/myVimrc
cp: cannot create regular file '/tmp/test/one/non-exist/backup/dir/myVimrc': No such file or directory
在上面的示例中,我们尝试将 .vimrc文件从当前用户的主目录复制到目标目录*/tmp/test/one/non-exist/backup/dir/*。
cp命令拒绝这样 做,因为目标目录不存在。
当我们复制文件时,这种情况经常发生:
- 首先,我们执行cp命令
- 然后,我们看到错误消息并意识到,“哦,我忘记创建目录了。”
- 我们执行mkdir 命令创建目标目录
- 最后,我们再次执行cp命令
显然,这是低效的。在本教程中,我们将在复制文件时自动创建不存在的目标目录。
我们将介绍实现这一目标的三种方法:
- 结合 mkdir和cp 命令
- 编写一个简单的shell函数来包装cp命令
- 使用 install 命令
3. 结合mkdir和 cp命令
mkdir命令是为创建目录而生的。 它有一个*-p*选项来创建我们需要的父目录。此外,如果目标目录已经存在,它不会报告错误。
此外,我们知道我们可以使用&& 运算符连接两个命令,例如cmd1 && cmd2。这样,只有当cmd1已成功执行(退出代码 = 0)时, 才会启动cmd2 。
因此,我们可以将mkdir和cp命令用*&&*连接起来, 这样我们就可以自动创建目标目录:
$ mkdir -p /tmp/test/one/non-exist/backup/dir && cp $HOME/.vimrc /tmp/test/one/non-exist/backup/dir/myVimrc
$ ls -l /tmp/test/one/non-exist/backup/dir/myVimrc
-rw-r--r-- 1 kent kent 3.4K Mar 19 14:50 /tmp/test/one/non-exist/backup/dir/myVimrc
如上面的输出所示,我们创建了目标目录并将文件一次性复制到其中。
但是,我们也可能注意到我们必须输入长路径*/tmp/test/one/non-exist/backup/dir*两次。这很不方便,特别是当目录不存在时,shell 的 tab 补全也对我们没有帮助。
现在,是时候介绍特殊的 Bash 变量*$_ *了。 下划线变量$_扩展为执行的最后一个命令的最后一个参数。
接下来,让我们看看如何使用这个特殊变量来简化我们的命令输入:
$ mkdir -p /tmp/test/one/non-exist/backup/dir && cp $HOME/.vimrc $_/myVimrc
$ ls -l /tmp/test/one/non-exist/backup/dir/myVimrc
-rw-r--r-- 1 kent kent 3.4K Mar 19 15:41 /tmp/test/one/non-exist/backup/dir/myVimrc
在这个例子中,我们在cp命令中使用了特殊的*$_*变量。
当我们执行cp命令时,最后执行的命令是*&&前面的mkdir*命令。
此外, mkdir命令的最后一个参数是目标目录,这正是我们不想再次键入的参数。
我们的mkdir和 cp组合现在更易于使用。然而,它仍然不完美。
通常,当我们要复制文件时,我们会键入类似*cp sourceFile targetFile *的内容。
但是使用我们的组合,我们必须先键入 mkdir,然后是没有目标文件的目标目录,然后是*&&运算符,以及带有特殊$_变量的cp*命令。
如果我们能更自然地实现我们的目标不是很好吗?
让我们在下一节中弄清楚。
4. 编写一个简单的 Bash 函数
在上一节中,我们了解到我们可以结合 mkdir -p和 cp在复制文件时自动创建目标目录。
但是我们不能自然地键入像cp sourceFile targetFile这样的命令。为了解决这个问题,我们可以将组合包装在一个 Bash 函数中:
CP() {
mkdir -p $(dirname "$2") && cp "$1" "$2"
}
在上面的代码片段中,我们声明了一个函数CP。它可以有两个参数,$1和*$2*。$1参数是我们要复制的源文件,而$ 2参数存储目标路径。
我们使用命令替换 $(dirname “$2”)来提取目标目录并将其传递给mkdir -p命令。
现在让我们将函数保存在文件func.sh中,获取文件,并测试它是否按预期工作:
$ source func.sh
$ CP $HOME/.vimrc /tmp/test/one/non-exist/backup/dir/myVimrc
$ ls -l /tmp/test/one/non-exist/backup/dir/myVimrc
-rw-r--r-- 1 kent kent 3464 Mar 19 17:08 /tmp/test/one/non-exist/backup/dir/myVimrc
正如我们在上面的输出中看到的,目标目录已自动创建,文件也已被复制。
*我们可以把这个函数放在我们的$HOME/.bashrc文件中,以便每次登录时自动获取它。**这样,我们可以使用CP*命令来复制文件,而不必关心“目标目录”存在的问题。
5.使用install命令
** install命令是*GNU coreutils 包的成员。因此,它适用于所有 Linux 发行版。*
我们可以使用 install命令来复制文件和设置文件属性。
与cp命令类似,install命令支持各种选项。但是,我们不会深入研究此命令的用法,并将其作为安装命令的教程。
我们希望在文件复制期间自动创建不存在的目标目录。这正是*-D*选项的用途:
$ install -D $HOME/.vimrc /tmp/test/one/non-exist/backup/dir/myVimrc
$ ls -l /tmp/test/one/non-exist/backup/dir/myVimrc
-rwxr-xr-x 1 kent kent 3464 Mar 19 17:40 /tmp/test/one/non-exist/backup/dir/myVimrc
是的!它按我们的预期工作。目录已创建,文件也已复制。
但是,如果我们检查上面的ls -l输出并将其与之前的输出仔细比较,我们会发现一些差异,即使myVimrc文件是从同一源复制的:
-rw-r--r-- 1 kent kent ... # <-- file copied by the cp command
-rwxr-xr-x 1 kent kent ... # <-- file copied by the install -D command
为什么安装命令复制的文件设置为自动可执行?
*这是因为install命令复制的文件默认具有“ -rwxr-xr-x ”(0755)权限。*我们可以使用-m选项设置目标文件的权限:
$ install -D -m 644 $HOME/.vimrc /tmp/test/one/non-exist/backup/dir/myVimrc
$ ls -l /tmp/test/one/non-exist/backup/dir/myVimrc
-rw-r--r-- 1 kent kent 3464 Mar 19 17:54 /tmp/test/one/non-exist/backup/dir/myVimrc
现在,复制的文件具有权限位644。