使用 find -exec 命令选项

评论 0 浏览 0 2020-11-27

1. 概述

在本教程中,我们将探讨 Linux find 命令的 -exec 参数。这个参数扩展了 find 的功能,并使其成为众所周知的瑞士军刀。

我们将讨论使用-exec来执行命令和shell函数,以及如何控制它们以提高它们的执行效率。

2. -exec 操作

find 命令由两个主要部分组成:表达式和操作。

当我们最初使用find时,我们通常从表达式部分开始。这部分允许我们指定一个过滤器来定义要选择的文件。

一个经典的例子是:

$ find Music/ -name *.mp3
Music/Gustav Mahler/01 - Das Trinklied vom Jammer der Erde.mp3
Music/Gustav Mahler/02 - Der Einsame im Herbst.mp3
Music/Gustav Mahler/03 - Von der Jugend.mp3
Music/Gustav Mahler/04 - Von der Schönheit.mp3
Music/Gustav Mahler/05 - Der Trunkene im Frühling.mp3
Music/Gustav Mahler/06 - Der Abschied.mp3

此命令将生成 Music 目录及其所有子目录中的 mp3 文件列表。

此示例中的操作部分是默认操作-print。此操作打印结果路径,中间带有换行符。如果没有指定其他操作,它将运行。

相比之下,-exec 操作允许我们在生成的路径上执行命令。

假设我们要在刚刚找到的 mp3 文件列表上运行 file 命令来确定其文件类型。我们可以通过运行以下命令来实现这一点:

$ find Music/ -name *.mp3 -exec file {} \;
Music/Gustav Mahler/01 - Das Trinklied vom Jammer der Erde.mp3:
  Audio file with ID3 version 2.4.0, contains:MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, Stereo
Music/Gustav Mahler/02 - Der Einsame im Herbst.mp3:
  Audio file with ID3 version 2.4.0, contains:MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, Stereo
Music/Gustav Mahler/03 - Von der Jugend.mp3:
  Audio file with ID3 version 2.4.0, contains:MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, Stereo
Music/Gustav Mahler/04 - Von der Schönheit.mp3:
  Audio file with ID3 version 2.4.0, contains:MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, Stereo
Music/Gustav Mahler/05 - Der Trunkene im Frühling.mp3:
  Audio file with ID3 version 2.4.0, contains:MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, Stereo
Music/Gustav Mahler/06 - Der Abschied.mp3:
  Audio file with ID3 version 2.4.0, contains:MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, Stereo

让我们剖析传递给 -exec 标志的参数,其中包括:

  1. 命令:file
  2. 占位符:{}
  3. 命令分隔符:\;

现在我们将深入探讨这三个部分。

3. 命令

任何可以被我们的 shell 执行的命令在这里都是可以接受的。

我们应该注意,这不是我们的 shell 执行命令,而是我们直接使用 Linux 的 exec 来执行命令。这意味着任何 shell 扩展在这里都不起作用,因为我们没有 shell。另一个影响是 shell 函数或别名不可用。

作为缺少 shell 函数的解决方法,我们可以导出它们并在文件中使用我们请求的函数调用 bash -c

为了看到这一点,我们将继续使用 Mahler 的 mp3 文件目录。让我们创建一个 shell 函数来显示曲目名称和一些有关质量的详细信息:

function mp3info() {
    TRACK_NAME=$(basename "$1")
    FILE_DATA=$(file "$1" | awk -F, '{$1=$2=$3=$4=""; print $0 }')
    echo "${TRACK_NAME%.mp3} : $FILE_DATA"
}

如果我们尝试对所有文件运行 mp3info 命令,-exec 会抱怨它不知道 mp3info

find . -name "*.mp3" -exec mp3info {} \;
find: ‘mp3info’: No such file or directory

如前所述,要解决此问题,我们需要导出 shell 函数并将其作为生成的 shell 的一部分运行:

$ export -f mp3info
$ find . -name "*.mp3" -exec bash -c "mp3info \"{}\"" \;
01 - Das Trinklied vom Jammer der Erde :      128 kbps  44.1 kHz  Stereo
02 - Der Einsame im Herbst :      128 kbps  44.1 kHz  Stereo
03 - Von der Jugend :      128 kbps  44.1 kHz  Stereo
04 - Von der Schönheit :      128 kbps  44.1 kHz  Stereo
05 - Der Trunkene im Frühling :      128 kbps  44.1 kHz  Stereo
06 - Der Abschied :      128 kbps  44.1 kHz  Stereo

请注意,由于我们的某些文件名包含空格,因此我们需要引用结果占位符。

4. 结果占位符

结果占位符由两个大括号 {} 表示。

如果需要,我们可以多次使用占位符:

find . -name "*.mp3" -exec bash -c "basename \"{}\" && file \"{}\" | awk -F: '{\$1=\"\"; print \$0 }'" \;
01 - Das Trinklied vom Jammer der Erde.mp3
  Audio file with ID3 version 2.4.0, contains MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, Stereo
02 - Der Einsame im Herbst.mp3
  Audio file with ID3 version 2.4.0, contains MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, Stereo
03 - Von der Jugend.mp3
  Audio file with ID3 version 2.4.0, contains MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, Stereo
04 - Von der Schönheit.mp3
  Audio file with ID3 version 2.4.0, contains MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, Stereo
05 - Der Trunkene im Frühling.mp3
  Audio file with ID3 version 2.4.0, contains MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, Stereo
06 - Der Abschied.mp3
  Audio file with ID3 version 2.4.0, contains MPEG ADTS, layer III, v1, 128 kbps, 44.1 kHz, Stereo

在上面的示例中,我们运行了 basename 以及 file 命令。为了让我们能够连接命令,我们生成了一个单独的 shell,如上所述。

5. 分隔符

我们需要为 find 命令提供分隔符,以便它知道我们的 -exec 参数在哪里停止。

可以为 -exec 参数提供两种类型的分隔符:分号 (;) 或加号 (+)。

由于我们不希望 shell 解释分号,因此需要对其进行转义 (\;)。

分隔符决定 find 处理表达式结果的方式。如果我们使用分号 (;),-exec 命令将分别对每个结果重复执行。另一方面,如果我们使用加号 (+),所有表达式的结果将被连接起来并作为一个整体传递给 -exec 命令,该命令只会运行一次。

让我们通过另一个例子来看看加号的用法:

$ find . -name "*.mp3" -exec echo {} +
./Gustav Mahler/01 - Das Trinklied vom Jammer der Erde.mp3 ./Gustav Mahler/02 -
  Der Einsame im Herbst.mp3 ./Gustav Mahler/03 - Von der Jugend.mp3 ./Gustav Mahler/04 -
  Von der Schönheit.mp3 ./Gustav Mahler/05 - Der Trunkene im Frühling.mp3
  ./Gustav Mahler/06 - Der Abschied.mp3

运行 echo 时,每个 echo 调用都会生成一个换行符,但由于我们使用了加号分隔符,因此只进行了一次 echo 调用。让我们将此结果与分号版本进行比较:

$ find . -name "*.mp3" -exec echo {} \;
./Gustav Mahler/01 - Das Trinklied vom Jammer der Erde.mp3
./Gustav Mahler/02 - Der Einsame im Herbst.mp3
./Gustav Mahler/03 - Von der Jugend.mp3
./Gustav Mahler/04 - Von der Schönheit.mp3
./Gustav Mahler/05 - Der Trunkene im Frühling.mp3
./Gustav Mahler/06 - Der Abschied.mp3

从性能角度来看,我们通常更喜欢使用加号分隔符,因为为每个文件运行单独的进程可能会导致 RAM 和处理时间的严重损失。

但是,在以下情况之一,我们可能更喜欢使用分号分隔符:

  • -exec 运行的工具不接受多个文件作为参数。
  • 一次在如此多的文件上运行该工具可能会占用太多内存。
  • 我们希望尽快开始获得一些结果,尽管需要更多时间才能获得所有结果。

六,结论

在本文中,我们学习了在 Linux 中运行 find 命令时如何使用 -exec 参数。我们解释了这个参数的各个部分,以及如何使用它们来有效地运行我们的命令。

最后更新2023-08-07
0 个评论
标签