如果使用错误的 Linux 通配符,您将面临数据丢失的风险
Linux 通配符使您可以键入同时作用于整组文件的单个命令。除非出现问题,否则这可以节省大量时间。他们可以。具有破坏性。
通配符的用途是什么
众所周知的通配符是问号 ? 和星号 *。这些可用于创建文件名模式。问号代表任何单个字符,星号代表任何字符序列,包括零个字符。
知道了这一点,我们就可以构造匹配多个文件名的模式。我们不是在命令行上输入所有文件名,而是输入模式。该命令将作用于与该模式匹配的所有文件。
如果我们在这样的目录中有一个文件集合:
我们可以选择与我们提供的模式匹配的文件组。
ls taf_*
这为我们提供了名称开头带有“taf_”的所有文件。
ls *.sh
ls s*.sh
第一个命令列出目录中的所有 shell 脚本文件。第二个命令仅列出以“s”开头的文件,这些文件也是 shell 脚本文件。
这一切看起来都很简单,使用 ls 确实如此。但其他命令可以使用这种类型的模式匹配。当 shell 在命令有机会之前尝试通过模式匹配提供帮助时,就会出现问题。
将星号与 find 命令一起使用
将模式扩展为匹配文件列表的操作称为通配。
它最初是 Unix 版本 6 中的一个独立命令,然后成为一个可以链接到其他程序的库,现在它是一个内置的 shell。模式的扩展由 shell 执行,扩展的结果作为命令行参数传递给命令。
我们将看两个使用 find 命令的示例。第一种做法符合您的预期,但第二种做法可能会让您大吃一惊。
在本例中,我们将使用一个包含单个文件的目录,名为 readme.txt。有两个目录,src 和 inc。它们包含 C、H、MD 和 TMP 文件的混合。
ls -R
我们可以使用 find 递归地查找名称与我们的模式 (-name *.c) 匹配的文件 (-type f),从而为我们提供 C 文件的列表。
find . -type f -name *.c
我们可以添加 -not 选项来反转搜索,向我们显示除 C 文件之外的所有内容。
find . -type f -not -name *.c
查看此列表后,我们选择删除除 C 文件之外的所有内容。我们可以通过添加 -delete 选项来做到这一点。
find . -type f -not -name *.c -delete
find .
第二个 find 命令递归列出当前目录及其下的所有内容。剩下的就是我们的 C 文件。
这就像我们大多数人所期望的那样。现在我们将做完全相同的事情,但这次当前目录中的文件不是文本文件,而是 C 文件。
ls -R
我们将使用相同的 find 命令和选项来删除除 C 文件之外的所有内容。这根本不是我们想要的。
find . -type f -not -name *.c -delete
find .
除了当前目录中的一个 C 文件之外,目录树中的每个文件都被愉快地删除了。
我们将再次重置文件,并按照我们应该使用的方式发出命令。
所有文件都已就位,并且我们在当前目录中有一个 C 文件,就像我们之前所做的那样。
ls -R
这次,我们将通配符模式用单引号引起来。
find . -type f -not -name '*.c' -delete
find .
这就是我们想要的。除了我们的 C 文件之外,一切都消失了。
好的,那么出了什么问题呢?
单引号阻止 shell 扩展文件名模式。它按原样传递给命令或程序,以便命令执行操作。
在有效的示例中,我们在当前目录中有一个 readme.txt 文件。 shell 无法找到与 *.c 匹配的内容,因此它通过 *.c 来查找并执行操作。
在删除除 C 文件之外的所有内容的示例中,我们在当前目录中有一个名为 main.c 的文件。 shell 将模式与该文件进行匹配,并将文件名传递给 find 命令。所以 find 的指令是删除所有不叫 main.c 的东西。
我们可以用一个小型 C 程序来说明这一点,该程序仅在终端窗口中显示其命令行参数。
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int i;
printf("You supplied %d arguments.\n", argc-1);
for (i=1; i<argc; i++)
printf("%-2d) \"%s\"\n", i, argv[i]);
exit (0);
}
我将其保存为名为 glob.c 的文件,并使用以下命令进行编译:
gcc -o glob glob.c
变量 argc 保存我们传递给程序的参数数量。 for 循环遍历参数列表并将每个参数打印到终端窗口。
for 循环从参数 1 开始,而不是从零开始。有一个参数为零。它始终保存二进制文件本身的名称。为了避免混淆水,我避免打印它。唯一打印的参数是我们在命令行上提供的参数。
./glob one two 3 ant beetle cockroach
让我们尝试使用 *.c 作为命令行参数。
ls *.c
./glob *.c
当前目录中没有任何 C 文件,shell 会将 *.c 传递给 find 命令。然后 find 命令作用于通配符模式本身。但是,当当前目录中有一个 C 文件时,shell 会将匹配的 C 文件的名称传递给程序。
ls *.c
./glob *.c
我们的程序接收 C 文件的名称作为其参数,find 命令也是如此。所以实际上,find 正在执行它被告知执行的操作:删除除 main.c 文件之外的所有文件。
这次,我们将通配符模式用单引号引起来。
ls *.c
./glob '*.c'
shell 忽略将其通配符应用于通配符模式的机会,并将其直接传递给命令以进行进一步处理。
一个简单的修复,你可以引用我的话
作为一般规则,请引用传递给 find 等命令的通配符模式。这就是防止此类潜在灾难性事故所需的全部措施。