返回目录中每个 pdf 文档的文件名和页数

返回目录中每个 pdf 文档的文件名和页数

上下文: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

(假设zshbash -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 正确编码的文本组成的文件名,这是这些格式的限制。

相关内容