在 bash for 循环中列出多种文件类型

在 bash for 循环中列出多种文件类型

我编写了一个脚本来转换文件夹中的图像。该脚本使用 for 循环:

我=“1”
对于 *.jpg 中的文件;执行
     输出文件=$(echo "图像"$(echo $i))
     转换“$file”-调整大小 50x50 $outputFile
     i=$[i+1]
完毕

我想要做的是允许脚本在多种文件类型扩展上运行。现在我尝试这样做:

对于 *.jpg *.JPG *.jpeg 中的文件;执行
....
完毕

我遇到的问题是,如果您所在的文件夹中全是 *.JPG 而没有 *.jpg,即使没有图像,脚本仍会触发 i+1,因为它无论如何都会运行 for 循环,如果找不到任何 *.jpg,它就会转到 *.JPG。

我如何才能定位多个文件扩展名,而无需它按类型自行运行?例如,它们的语法是否类似于:

对于 [*.jpg | *.JPG] 中的文件;执行
...

?
这样,我的输出文件夹将始终包含带有如下标签的图像:
image1.jpg
image2.jpg
image3.jpg
等等。

而不是因为没有 *.jpg 可以先运行而导致文件夹缺少 image1.jpg。

答案1

解决方案

使用 nullglob。将以下行放在循环之前for

shopt -s nullglob

nullglob意味着如果没有这样的文件,则从列表中删除 glob。观察没有 nullglob 的情况:

$ echo  *.jpg *.JPG *.jpeg
test.jpg *.JPG *.jpeg

现在,使用 nullglob:

$ shopt -s nullglob
$ echo  *.jpg *.JPG *.jpeg
test.jpg

修改后的脚本

完整的脚本可能是:

i=1
shopt -s nullglob
for file in *.jpg *.JPG *.jpeg; do
     convert "$file" -resize 50x50 "image$i.jpg"
     i=$((i+1))
done

三点说明:

  1. 在 的定义中outputFile,所有这些echo陈述都是不必要的。

  2. 按照惯例,我在输出文件名中添加了扩展名.jpg。如果您确实不想要该扩展名,只需将其删除即可。

  3. $[...]用更标准的形式替换:$((...))

答案2

  1. 由于您想寻找多个清楚的(不区分大小写)扩展, John1024 的回答包含shopt -s nullglob 非常好用。

  2. 如果你只是在寻找*.jpg*.JPG和其他六种大小写字母的组合,最简单的方法就是

     for file in *.[Jj][Pp][Gg]
    

    查找名称为

    • (某事),然后
    • .(句号),然后是
    • Jj,然后
    • Pp,然后
    • G或者g

    换句话说,

    • (某事),然后
    • .(句号),然后是
    • “jpg”,以大写和小写字母的组合形式呈现
  3. 解决你的问题的方法之一是

     shopt -s nullglob
     for file in *.[Jj][Pp][Gg] *.[Jj][Pp][Ee][Gg]
     do …
    
  4. 这个[…]技巧很有用,但还有更好的方法。如果我告诉你有一个名为的 shell 选项nocaseglob,你能猜出它的作用吗?

     shopt -s nullglob nocaseglob
     for file in *.jpg *.jpeg
     do …
    

    所以现在*.jpg意味着

    • (某事),然后
    • .(句号),然后是
    • “jpg”,以大写和小写字母的组合形式呈现

    因此上述命令将匹配*.jpg*.JPG*.Jpg*.jpeg*.jPeG等。

  5. 另一种方法是使用extglobshell 选项来启用扩展模式匹配运算符。你仍然需要nullglobnocaseglob。扩展模式匹配运算符都看起来像

    (一些特殊字符)  模式列表  

    为了清晰起见,添加了空格,并且pattern-list是以下列表patterns 用 | 字符分隔。对你的目的来说最有趣的是

    @(模式列表

    这意味着匹配列出的模式之一。所以现在脚本变成了

     shopt -s nullglob nocaseglob extglob
     for file in *.@(jpg|jpeg)
     do …
    

    这看起来可能要多输入一些代码,但随着扩展数量的增加,权衡也会发生变化。考虑一下,

     for file in *.@(jpg|jpeg|jfif|exif|tif|tiff|gif|bmp|png|svg)
    

     for file in *.jpg *.jpeg *.jfif *.exif *.tif *.tiff *.gif *.bmp *.png *.svg
    
  6. 你从哪里学会的$(echo …)?当然,肯定有用,甚至有时有用,但你用得太多了。说$(some_command(s) …)$(echo …)

     outputFile="image$i"
    

    你甚至可能不需要引号(在赋值语句中)。

  7. 好的,你可以原谅仅此一次,因为您知道 的值outputFile是良性的,因为您将其设置为非空白、非特殊字符的常量字符串(image)加上一个变量,您将该变量设置为非空白、非特殊字符的常量字符串(1),并且只对其进行了简单的算术运算。但是,作为一般规则,您应该总是引用所有对 shell 变量的引用,除非你有很好的理由不这样做,并且你确定你知道自己在做什么。所以你的convert命令,理想情况下应该是

     convert "$file" -resize 50x50 "$outputFile"
    

    (带有引号$outputFile)。

答案3

您可能想要替换此部分:

for file in *.jpg; do

这样:

for file in $(ls *.jpg *.JPG); do

相关内容