编写一个 shell 脚本,将当前目录中包含单词“hello”的所有文件移动到名为“hello-world”的单独文件夹中

编写一个 shell 脚本,将当前目录中包含单词“hello”的所有文件移动到名为“hello-world”的单独文件夹中

编写一个 shell 脚本,将当前目录中包含该单词的所有文件移动hello到名为 的单独文件夹中hello-world

当前目录包含文件a b c d ef其中的文件ac并且f包含单词hello

如何在当前目录的文件中搜索单词hello并将满足条件的文件移动到另一个目录中hello-world

答案1

让我们把这个分解成更小的任务。我们需要

  • hello-world如果目录不存在则创建目录
  • 可靠地识别包含文本的文件hello
  • 可靠地将文件移动到新目录。

创建目录是通过 完成的mkdir。它将始终拒绝创建已存在的目录(从而拒绝覆盖此类目录),但它也有一个标志-p,如在man mkdir

-p, --parents
       no error if existing, make parent directories as needed

不会告诉您尝试创建的目录是否已存在。

因此我们的脚本的第一个命令可能是(首先检查您是否在正确的目录中)。

mkdir -p hello-world

第二步是确定我们想要的文件。在文件中搜索文本最明显的命令是grep。如果我们只想要包含 的文件的文件名hello,我们可以使用-l标志,根据man grep

-l, --files-with-matches
       Suppress  normal output; instead print the name of each input file
       from which output would normally have been printed.  The scanning
       will stop on the first match.

抑制正常输出。但是,虽然我们可以在您的示例中使用它,但我们真的不希望将文件名作为输出,因为命令的输出只是文本,而文件名可能包含各种字符(从简单的空格到奇特的换行符),这将导致 shell 看到与您要执行操作的文件的实际名称不同的内容,并以您不希望的方式运行。例如,为您的脚本设置了一个测试目录,如果我添加一个名称中包含空格的文件,请看会发生什么:

$ echo hello > with\ space
$ ls
a  b  c  d  e  f  with space
$ for i in $(grep -l hello *); do echo "$i"; done
a
c
f
with
space

该文件with space被视为两个独立的文件。

为了识别文件以便让 shell 处理它们,我们应该避免解析文件名。我们可以测试每个文件,看看它是否包含文本,如果包含,则移动它。要循环遍历文件,如上所示,我们可以使用for具有如下语法的 which:

for var in things; do stuff $var; done

for循环中(以及其他任何地方),我们可以使用该test命令,它有时看起来像[并且也像[[制作条件语句,如果您只想检查文件是否为空,或者是特定类型,我建议您使用该test命令。

但是您想要查找一些特定的文本,所以我们应该调用grep,但我们真正想知道的只是在文件中查找是否hello成功,所以我们知道我们必须对文件做些什么。

要根据命令的成功来创建条件语句,我们可以使用if.经常与//一起if使用,但您不需要它们,因为还有另一个有用的标志:test[[[grep

-q, --quiet, --silent
       Quiet; do not write anything to standard output. Exit immediately
       with zero status if any match is found, even if an error  was detected.

Bash 中的零表示成功。因此,为了完成我们想要的操作,我们可以编写如下代码

if grep -q hello file; then mv file hello-world; fi

fi是命令语法的一部分if。它告诉 shell 您已经完成了if

我通常在交互式 shell 中编写 shell 脚本,并且稍后才将它们脚本化到文件中,因为我很懒,而且我大多只编写非常简单的脚本。无论如何,您可以通过用 分隔命令来将简短的脚本编写为一行命令;。如果出现问题,请按向上箭头键编辑最后一个命令...

我们不想为每个文件运行一次该命令。这将违背编写脚本来完成这项工作并节省我们输入的目的,因此要循环遍历文件,我们可以使用。为了避免从目录for获取错误,我们可以添加另一个标志来排除它。grephello-world

这是我执行此项工作的测试命令以及我在测试环境中获得的输出:

$ mkdir -p hello-world; for file in *; do if grep -q --exclude-dir=hello-world -- hello "$file"; then echo mv -v -- "$file" hello-world; fi; done
mv -v -- a hello-world
mv -v -- c hello-world
mv -v -- f hello-world
mv -v -- with space hello-world

此脚本不会移动任何文件,因为echobeforemv显示了每次循环迭代将运行哪些命令。echo如果命令看起来正确,则在测试后删除(请注意,您不能总是可靠地使用它echo进行测试,并且您可能需要引入一些临时引用才能这样做,但它在这里工作正常)。

*是当前目录中所有非隐藏文件。使用./*含义相同的更安全,但要确保所有路径都以./(即当前工作目录的地址)开头,防止以 开头的文件名被解释为选项。我们已通过在命令中添加和来指示选项的结束.-处理这种可能性。是的详细标志。--grepmv-vmv

我们应该用引号括住变量,以抑制 shell 扩展。如果我删除 周围的引号"$file",我的输出是

mv -v -- a hello-world
mv -v -- c hello-world
mv -v -- f hello-world
grep: with: No such file or directory
grep: space: No such file or directory

以下是脚本:

#!/bin/bash

mkdir -p hello-world
for file in *; do
    if grep -q --exclude-dir=hello-world -- hello "$file"; then
        echo mv -v -- "$file" hello-world
    fi
done

echo当您准备真正移动文件时,请不要忘记删除。

PS,可能还有更有效的方法。我的方法只是举个例子。

答案2

这将是按照您的想法使用 grep 和 xargs 的解决方案:

#!/bin/bash
mkdir hello-world
grep -l hello * | xargs mv -t hello-world

答案3

从名为test.sh

#!/bin/sh
IFS=$'\n'
for i in $( grep -l hello --exclude=*.sh --exclude-dir=hello-world * ); do mv $i hello-world/; done;

从命令行:

IFS=$'\n' && for i in $( grep -l hello --exclude-dir=hello-world * ); do mv $i hello-world/; done;

希望它能帮助你...

请考虑创建第一个目录 hello-world。

相关内容