将 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 {} \;
来加快速度。
是的,xargs
比 find -exec {} \;
快,不是因为它做同样的工作效率更高,而是因为它做的工作不同!
find -exec {} \;
为每个文件调用一次(getfattr file1
,然后是 getfattr file2
,依此类推)。
xargs
将尽可能多的文件塞进一次调用中 (getfattr file1 file2 file3 ...
)。
使用 find -exec {} +
可以实现相同的行为(甚至更多的加速)——不需要为此使用 xargs
。
使用 xargs
和 find -exec {} +
,您将失去对输出格式的控制。只有一次 getfattr
调用,因此程序决定在 file1
、file2
等之间打印什么。 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 -n
或 grep -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"
我有一个脚本,我正在尝试使用 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 {} \;
来加快速度。
是的,xargs
比 find -exec {} \;
快,不是因为它做同样的工作效率更高,而是因为它做的工作不同!
find -exec {} \;
为每个文件调用一次(getfattr file1
,然后是getfattr file2
,依此类推)。xargs
将尽可能多的文件塞进一次调用中 (getfattr file1 file2 file3 ...
)。
使用find -exec {} +
可以实现相同的行为(甚至更多的加速)——不需要为此使用xargs
。
使用 xargs
和 find -exec {} +
,您将失去对输出格式的控制。只有一次 getfattr
调用,因此程序决定在 file1
、file2
等之间打印什么。 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 -n
或 grep -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
.
处理异常文件名
至少我在 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"