上下文:Catalina MacOS:目录中包含一组 .pdf 文件。某些文件名包含空格(xargs.txt 有问题)
从命令行:目标是返回每个 .pdf 文件的文件名和页数。
此代码片段返回一个可以通过管道传输到的文件名列表参数:
find . -type f -name '*.pdf'
此片段返回页数:
pdfinfo foo.pdf | grep Pages | awk '{print $2}'
pdftk foo.pdf dump_data | grep Pages | awk '{print $2}'
如何将片段与 xargs 一起使用以仅处理文件名中可能包含空格的 .pdf 文件?
失败的:
find . -name '*.pdf' | xargs pdfinfo |
代码片段打印文件名(尽管无法处理带空格的名称)并且不会在与文件名相同的行上打印页码:
find . -name '*.pdf' | xargs -I % sh -c 'echo %; pdfinfo % | grep Pages'
答案1
某些文件名包含空格(xargs.txt 有问题)
您只需使用-print0
选项find
和-0
选项xargs
。他们确实应该在其手册页的顶部对这些进行广告宣传!
find -iname '*.pdf' -print0 | xargs -0 ...
就可以了。这些选项告诉find
用零字节而不是换行符分隔找到的文件名。与空格、换行符、冒号等不同,零字节是不是允许在文件名中使用,因此这是分隔文件名的安全方法。-0
告诉xargs
期望零字节作为分隔符(而不是其他)。
然而,这是 OS X,你的 shell 可能运行得很好;所以,find
根本不需要。
#!/usr/bin/zsh -
for pdffile in **/*.pdf(N-.) ; do
print -r -- "${pdffile}" # This is already problematic again. Your file names
# might contain newlines, spaces etc, so no easy way
# to tell where file name ends and page count starts
pdfinfo -- "${pdffile}" | grep Pages | awk '{print $2}'
done
请注意双倍的"
您尝试使用的引号'
:您的代码无法工作,因为'
- 封装的字符串不会进行变量扩展,因此该字符串按原样(包括美元符号和变量名称)传递给正在调用的程序。这就是您想要的参数awk
,因为您需要传递$
,但不是您实际想要扩展变量内容的地方。
请注意,模糊绘图的答案是正确的,您可以将 grep 吸收到awk
调用中。您还应该确保正则表达式尽可能精确。
我们还可以通过生成 0 分隔的输出来解决“文件名可以包含空格、换行符和数字,因此在我的输出中我无法判断文件名的开始和结束位置”问题:
#!/usr/bin/zsh
for pdffile in **/*.pdf(N-.) ; do
pages=$(pdfinfo -- "${pdffile}" | awk '/^Pages:/{print $2}')
printf '%s\0\%d\0' "${pdffile}" "${pages}"
done
(您仍然可能遇到 PDF 文件的问题,其创作者或者制片人contains <newline>Pages:
,但至少,通过使用上面严格的正则表达式,我们已经最大限度地降低了风险)。
答案2
您可以使用find
的-exec
命令来运行pdfinfo
,然后将结果通过管道传输到awk
它可以自行进行模式匹配,而不需要grep
作为中间步骤:
find . -type f -name '*.pdf' -exec pdfinfo '{}' \; | awk '/Pages/ {print $2}'
当然,这只给出了页数,我现在看到对于每个文件,您都需要文件名 和总页数。我认为xargs
这里没有帮助,但while
循环可以完成这项工作:
#!/bin/sh
find . -type f -name '*.pdf' | while read -r f; do
p=$(pdfinfo "$f" | awk '/Pages/ {print $2}')
printf '%s\n' "$f $p"
done
答案3
如果您不需要遍历目录树,则此for
循环可能会执行以下操作:
for FN in *pdf; do pdfinfo "$FN" | awk '/^Pages/ {print ARGV[2], $2; exit}' - "$FN"; done
答案4
和exiftool
:
exiftool -r -ext pdf -q -p '$PageCount $Directory/$Filename' .
-r
(对于递归)与 结合使用-ext pdf
会执行类似于 的操作find . -name '*.pdf'
。
这样有利于展示。
对于可后处理的输出,例如在 shell 循环中,您宁愿使用一些 NUL 分隔的输出格式:
exiftool -r -ext pdf -q -if 'print "$PageCount/$Directory/$Filename\0";0' . |
while IFS=/ read -rd '' page file; do
something with "$page" and "$file"
done
(假设zsh
或bash -O lastpipe
)
或者它支持的一些序列化格式,例如 json、xml 或 php1:
$ exiftool -r -ext pdf -q -j -PageCount .
[{
"SourceFile": "./a.pdf",
"PageCount": 4
},
{
"SourceFile": "./a\nb.pdf",
"PageCount": 4
}]
$ exiftool -r -ext pdf -q -X -PageCount .
<?xml version='1.0' encoding='UTF-8'?>
<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
<rdf:Description rdf:about='./a.pdf'
xmlns:et='http://ns.exiftool.ca/1.0/' et:toolkit='Image::ExifTool 11.88'
xmlns:PDF='http://ns.exiftool.ca/PDF/PDF/1.0/'>
<PDF:PageCount>4</PDF:PageCount>
</rdf:Description>
<rdf:Description rdf:about='./a
b.pdf'
xmlns:et='http://ns.exiftool.ca/1.0/' et:toolkit='Image::ExifTool 11.88'
xmlns:PDF='http://ns.exiftool.ca/PDF/PDF/1.0/'>
<PDF:PageCount>4</PDF:PageCount>
</rdf:Description>
</rdf:RDF>
exiftool -r -ext pdf -q -php -PageCount .
Array(Array(
"SourceFile" => "./a.pdf",
"PageCount" => 4
),
Array(
"SourceFile" => "./a\nb.pdf",
"PageCount" => 4
));
(此处以包含换行符的文件名为例)。
但请注意,JSON 和 XML 仅适用于由以 UTF-8 正确编码的文本组成的文件名,这是这些格式的限制。