【问题标题】:Format xargs output to grep将 xargs 输出格式化为 grep
【发布时间】:2020-10-01 17:17:43
【问题描述】:

我有一个脚本,我正在尝试使用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 返回的整个输出按文件“保留”,因为我需要具有匹配扩展属性的文件名,然后将其传递给sed 以提取它。在某些情况下,如果我需要在扩展属性中搜索具有 multiple 匹配项的文件,我会依次执行多个 grep 命令。问题是输出:

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

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

示例

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

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

如果我尝试使用+ 方法find 的输出grep,例如质量值为“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 仍然在 grep 后打印输出。换句话说,find 可以找到 1000 个 mp4 文件,其中可能有 20 个具有 user.quality="10" 条目,但即使应用 grep 搜索该字符串仍然返回 1000 个文件名(在 sed 之后)。

不会在使用\; 时发生。我从grep 得到的唯一结果是:

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

这是预期的行为。

【问题讨论】:

  • 您能否添加一个具有预期输入(目录结构)和预期输出的示例?
  • 我添加了一个例子来说明我的意思。

标签: linux bash grep xargs xattr


【解决方案1】:

xargsfind -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) 总是在一行中打印文件名。换行符使用\012 编码,反斜杠使用\134 编码。因此,可以安全处理这些文件。

上述命令有效,但仅打印编码文件名。要获得实际的文件名,您必须扩展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"

【讨论】:

  • 值得一提的是,虽然速度提升幅度很大,但它仍然只适用于单个属性。如果您需要过滤多个属性,由于getfattr 的限制,必须使用较慢的\; 方法。
  • 不,即使过滤多个属性,您仍然可以使用find -exec getfattr -d ... +。你只需要调整你的过滤逻辑。 getfattr 相对容易安全解析。
猜你喜欢
  • 2020-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-11
  • 2018-11-13
  • 1970-01-01
  • 2023-01-28
  • 2019-08-24
相关资源
最近更新 更多