find -exec 命令选项与基本名称

find -exec 命令选项与基本名称

我有以下 JPEG 文件:

$ ls -l
-rw-r--r-- 1 user group 384065 janv. 21 12:10 CamScanner 01-10-2022 14.54.jpg
-rw-r--r-- 1 user group 200892 janv. 10 14:55 CamScanner 01-10-2022 14.55.jpg
-rw-r--r-- 1 user group 283821 janv. 21 12:10 CamScanner 01-10-2022 14.56.jpg

我用来$ img2pdf将每个图像转换为 PDF 文件。要做到这一点 :

$ find . -type f -name "*.jpg" -exec img2pdf "{}" --output $(basename {} .jpg).pdf \;

结果 :

$ ls -l *.pdf
-rw-r--r-- 1 user group 385060 janv. 21 13:06 CamScanner 01-10-2022 14.54.jpg.pdf
-rw-r--r-- 1 user group 201887 janv. 21 13:06 CamScanner 01-10-2022 14.55.jpg.pdf
-rw-r--r-- 1 user group 284816 janv. 21 13:06 CamScanner 01-10-2022 14.56.jpg.pdf

如何删除.jpgPDF 文件名的一部分?即,我想要CamScanner 01-10-2022 14.54.pdf又不想要CamScanner 01-10-2022 14.54.jpg.pdf

单独使用,$ basename filename .extension打印不带扩展名的文件名,例如:

$ basename CamScanner\ 01-10-2022\ 14.54.jpg .jpg
CamScanner 01-10-2022 14.54

$ find但似乎语法在我的命令中不起作用。知道为什么吗?

注意:如果您替换$ img2pdf$ echo相同的,$ basename则不会删除该.jpg部分:

$ find . -type f -name "*.jpg" -exec echo $(basename {} .jpg).pdf \;
./CamScanner 01-10-2022 14.56.jpg.pdf
./CamScanner 01-10-2022 14.55.jpg.pdf
./CamScanner 01-10-2022 14.54.jpg.pdf

答案1

您的命令的问题find在于周围的命令替换basename是由 shell 执行的它甚至开始运行find(作为评估参数应该find是什么的一个步骤)。

每当您需要运行除带有可选参数的简单实用程序之外的任何内容时find,例如,如果您需要执行任何管道、重定向或扩展(如您的问题中所示),您将需要使用 shell 来执行这些操作事物:

find . -type f -name '*.jpg' \
    -exec sh -c 'img2pdf --output "$(basename "$1" .jpg).pdf" "$1"' sh {} \;

或者,更有效(每次调用sh -c都会处理一批找到的路径名),

find . -type f -name '*.jpg' -exec sh -c '
    for pathname do
        img2pdf --output "$(basename "$pathname" .jpg).pdf" "$pathname"
    done' sh {} +

或者,与zsh,

for pathname in ./**/*.jpg(.DN); do
    img2pdf --output $pathname:t:r.png $pathname
done

这使用 globbing 限定符.DN仅匹配常规文件 ( .),允许匹配隐藏名称 ( D),并在未找到匹配项时删除模式 ( N)。然后,它使用:t修饰符提取 的“尾部”(文件名组件)$pathname:r提取结果基本名称的“根”(无文件名后缀),然后添加.png到末尾。

请注意,上述所有变化都会将输出写入当前目录,无论在何处找到 JPEG 文件。如果所有 JPEG 文件都在当前目录中,则绝对不需要使用find,并且您可以对通配模式的扩展使用简单的循环*.jpg

for pathname in ./*.jpg; do
    img2pdf --output "${pathname%.jpg}.png" "$pathname"
done

参数替换从 值的末尾${pathname%.jpg}删除。您可能想使用此替换.jpg$pathname代替 basename如果您想将输出写入找到 JPEG 文件的原始目录,在您使用find多个目录的情况下,例如,类似

find . -type f -name '*.jpg' -exec sh -c '
    for pathname do
        img2pdf --output "${pathname%.jpg}.pdf" "$pathname"
    done' sh {} +

也可以看看:

答案2

从务实的角度来看,一旦您达到问题末尾所描述的状态:

./CamScanner 01-10-2022 14.56.jpg.pdf
./CamScanner 01-10-2022 14.55.jpg.pdf
./CamScanner 01-10-2022 14.54.jpg.pdf

您可以选择使用rename来获取所需的最终文件名:

~ rename 's/jpg.pdf/pdf/'

答案3

@Ulrich Schwarz 的评论很恰当。为了使其完整,我们假设您没有任何带引号或单引号的文件名。

调整您的find语法以简单地输出不含 .jpg 的基本名称,然后在适当的情况下使用和扩展名awk来重建img2pdf语法:.jpg.pdf

find命令将输出裸基名称:

$ find . -type f -name "*.jpg" -exec basename {} .jpg \;
CamScanner 01-10-2022 14.56
CamScanner 01-10-2022 14.55
CamScanner 01-10-2022 14.54

现在将这些基本名称传递给awk并让 awk 构造正确的语法img2pdf

$  find . -type f -name "*.jpg" -exec basename {} .jpg \; | \
    awk '{print "cp -vp '\''" $0 ".jpg'\'' --output '\''" $0 ".pdf'\''"}'
img2pdf 'CamScanner 01-10-2022 14.56.jpg' --output 'CamScanner 01-10-2022 14.56.pdf'
img2pdf 'CamScanner 01-10-2022 14.55.jpg' --output 'CamScanner 01-10-2022 14.55.pdf'
img2pdf 'CamScanner 01-10-2022 14.54.jpg' --output 'CamScanner 01-10-2022 14.54.pdf'

如果该语法看起来没问题,则将其通过管道传输到您最喜欢的 shell。

相关内容