コンテキスト: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
optionsfind
と-0
optionsを使用してくださいxargs
。彼らはマンページの上部にこれを宣伝する必要があります!
find -iname '*.pdf' -print0 | xargs -0 ...
それはすべてです。このオプションは、find
見つかったファイル名を改行ではなくゼロバイトで区切るように指示します。スペース、改行、コロンなどとは異なり、0バイトはいいえファイル名には許可されるため、ファイル名を区別する安全な方法です。区切り文字でゼロバイトを期待するように-0
指示します。xargs
しかし、これはOS Xで、シェルはおそらくうまく機能するため、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
呼び出しに吸収できます。また、正規表現ができるだけ正確であることを確認する必要があります。
また、ゼロで区切られた出力を生成して、「ファイル名にスペース、改行、数字を含めることができるため、出力でファイル名が開始および終了する場所がわからない」という問題を解決することもできます。
#!/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ファイルにはまだ問題がある可能性があります。創作者または生産者含まれます<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'
。
プレゼンテーションに最適です。
シェルループなどの後処理可能な出力の場合は、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で適切にエンコードされたテキストで構成されたファイル名でのみ機能します。これはこれらの形式の制限です。