将 xargs 输出格式化为 grep

Format xargs output to grep

我有一个脚本,我正在尝试使用 xargs 对其进行优化。当前版本使用find配合-exec调用命令:

find -type f -iname "*.mp4" -print0 -printf '\n' -exec getfattr -d --absolute-names {} \;

之后我可以通过管道传送到 grep,例如:

grep -z -P user\.md5\=\"$input_search_hash\"

过滤结果,同时保持整个输出 -z

我需要每个文件从 getfattr 返回的整个输出是 "preserved",因为我需要有匹配扩展属性的文件名,然后传递给 sed 来提取它。如果我需要搜索在扩展属性中具有 多个 匹配项的文件,也会有多个 grep 命令顺序排列的情况。问题是输出:

find -type f -iname "*.mp4" -print0 | xargs -0 getfattr -d --absolute-names

未按 grep 将以这种方式过滤的方式格式化。 -exec 方法一起工作。我可以将附加选项传递给 xargs 或通过管道输入一些附加命令,这些命令将格式化输出以使 grep 正确复制 -exec 的行为吗?我猜想在输入 grep 之前我需要某种换行符,就像 -printf '\n'-exec 方法中所做的那样。我只想使用 getfattr 到 "search" 扩展属性,而不需要 grep 输出,但它无法通过提供 xattr 名称和值来做到这一点。

例子

输入来自find命令,它是任意目录结构中的视频文件列表。每个 getfattr 命令的输出,对于每个文件都是这样的:

# file: /path/to/file/test.mp4
user.md5="0e29a7f555af518872771689e28d998d"
user.quality="10"
user.sha256="d49ba58e3b30f4ef8c81d19ce960edcf6552977bb8adb79b5b9a677ba9a54b2b"
user.size="1645645"

如果我尝试使用 + 方法 grep find 的输出,假设质量值为“10”,我将得到如下结果:

# file: /path/to/file/test.mp4
user.md5="8cf97b888e6fdbed27b02233cd6779f5"
user.quality="12"
user.sha256="613d16b2a0270e2e5f81cfd58b1eacf710a65b82ce2dab49a1e415275440f429"
user.size="1645645"

# file: /path/to/file/test1.mp4
user.md5="3c5a39f1ceefce1e124bcd6786a99155"
user.quality="10"
user.sha256="0d7128a7642d24ea879bbfb3de812b7939b618d8af639f07d5104c954c8049c3"
user.size="5674567"

# file: /path/to/file/test2.mp4
user.md5="0e29a7f555af518872771689e28d998d"
user.quality="6"
user.sha256="d49ba58e3b30f4ef8c81d19ce960edcf6552977bb8adb79b5b9a677ba9a54b2b"
user.size="15645"

返回 find 找到的所有 个文件,并突出显示要从 grep 中搜索的字符串,在本例中为 user.quality="10" ,但其他文件 test.mp4 和 test2.mp4 仍然打印输出 post-grep。换句话说,find 可能找到 1000 个 mp4 文件,其中可能有 20 个有 user.quality="10" 条目,但即使应用 grep 搜索该字符串仍然 returns 1000 个文件名(在sed)。

使用 \; 不会 发生这种情况。我唯一想从 grep 中得到的是:

# file: /path/to/file/test.mp4
user.md5="3c5a39f1ceefce1e124bcd6786a99155"
user.quality="10"
user.sha256="0d7128a7642d24ea879bbfb3de812b7939b618d8af639f07d5104c954c8049c3"
user.size="5674567"

这是预期的行为。

xargs 对比 find -exec

在我看来,您想使用 xargs 而不是 find -exec {} \; 来加快速度。

是的,xargsfind -exec {} \; 快,不是因为它做同样的工作效率更高,而是因为它做的工作不同!

  • find -exec {} \; 为每个文件调用一次(getfattr file1,然后是 getfattr file2,依此类推)。
  • xargs 将尽可能多的文件塞进一次调用中 (getfattr file1 file2 file3 ...)。
    使用 find -exec {} + 可以实现相同的行为(甚至更多的加速)——不需要为此使用 xargs

使用 xargsfind -exec {} +,您将失去对输出格式的控制。只有一次 getfattr 调用,因此程序决定在 file1file2 等之间打印什么。 getfattr 没有自定义输出格式的选项。

没问题!你可以...

解析getfattr的输出

...很容易。
对于初学者,我们假设所有路径名都很正常。空格、*? 都可以。对于包含反斜杠和换行符的非常不寻常的路径名,请参阅最后一节。

如果您使用 -n user.md5 而不是 -d 仅输出相关属性,那么您就知道每个文件的输出(如果有的话)始终采用

形式
# file: path in a single line
user.md5=encoded value of the attribute

没有属性 user.md5 的文件根本不打印。它们会在 stderr 上发出警告,可以通过 2> /dev/null.

抑制

现在,grep 用于匹配属性。使用 grep -B1 也可以打印每个匹配项上方的行(即路径)。然后使用 sed -ngrep -o 提取文件名。

find -type f -iname '*.mp4' -exec getfattr -n user.md5 --absolute-names {} + 2> /dev/null |
grep -B1 -Fx "user.md5=\"$input_search_hash\"" |
sed -n 's/^# file: //p'

以上命令打印所有具有属性 user.md5 且值为 $input_search_hash.

的 mp4 文件的路径

处理异常文件名

至少我在 Debian 10 上的版本 (getfattr 2.4.48 by Andreas Gruenbacher) 总是在一行中打印文件名。换行符使用 2 编码,反斜杠使用 4 编码。因此,可以安全地处理这些文件。

以上命令有效,但仅打印编码文件名。要获得实际的文件名,您必须扩展 sed 命令或向 interpret octal escape sequences 添加另一个命令。对我来说,getfattr 只会转义 \n\r\,因此 sed 's:\012:\n:g;s:\015:\r:g;s:\134:\:g' 应该足以打印。为了进一步处理,您可能需要使用 tr \n \0 | sed -z ...,这样文件名由空字节分隔。

要测试为您转义了哪些字符,请创建一个包含所有允许字节的文件名并让 getfattr 打印其名称:

f=$(printf $(printf '\%o' $(seq 1 255)) | tr -d /)
touch "$f"
setfattr -n user.md5 -v 123 "$f"
getfattr -n user.md5  "$f"
rm "$f"