如何在Bash脚本中检查文件名的扩展名?

246
我正在编写一个Bash夜间构建脚本。除了一个小问题,一切都很好:


#!/bin/bash

for file in "$PATH_TO_SOMEWHERE"; do
      if [ -d $file ]
      then
              # do something directory-ish
      else
              if [ "$file" == "*.txt" ]       #  this is the snag
              then
                     # do something txt-ish
              fi
      fi
done;

我的问题是确定文件扩展名,然后相应地采取行动。我知道问题出在if语句中,测试txt文件。

如何确定文件是否具有.txt后缀?


如果文件名中有空格,这将会出错。 - jfg956
除了Paul的答案之外,您还可以使用$(dirname $PATH_TO_SOMEWHERE)$(basename $PATH_TO_SOMEWHERE)来拆分文件夹和目录,并执行某些目录式和文件式的操作。 - McPeppr
11个回答

374

制作

if [ "$file" == "*.txt" ]

像这样:

if [[ $file == *.txt ]]

也就是说,使用双括号而不是引号。

== 右侧是一个 shell 模式。 如果需要正则表达式,请使用 =~


16
我不知道这件事。似乎这是一个特殊情况,即“==”或“!=”的右边会被扩展为一个Shell模式。个人认为这比我的答案更清晰易懂。 - Paul Stephenson
28
我对Bash不太熟悉,花了一点时间才弄清楚如何在多条件的if语句中使用它。我在这里分享一下,以防对某人有所帮助。if [[ ( $file == *.csv ) || ( $file == *.png ) ]] - joelostblom
14
@cheflo 一般而言,这对于多种情况都是有好处的。在这个具体情况中,你也可以使用 if [[ $file =~ .*\.(csv|png) ]]。它更短、更清晰、更容易添加其他扩展名,并且可以轻松地进行配置(将 "csv|png" 放入变量中)。 - jox
13
你可以在文件名两侧加上双引号。if [[ "$file" == *.txt ]] 如果文件名中包含空格,就必须使用双引号。 - shawnhcorey
3
不必引用$file,即使它包含空格。[[命令不执行单词拆分(也不执行路径名扩展)。这是[[和[之间的主要区别之一,也是使用[[的主要原因之一。 - Hontvári Levente
显示剩余2条评论

309

我想您想要问的是:“$file的最后四个字符是否等于.txt?” 如果是的话,您可以使用以下代码:

if [ "${file: -4}" == ".txt" ]

请注意file:-4之间的空格是必需的,因为 ':-' 修饰符的含义不同。


1
为此,您也可以在Windows机器上将command.com重命名为command.txt。 - hometoast
12
如果您想指定一个不等式,请记得包含额外的括号:if [[ ${file: -4} != ".txt" ]] - Ram Rajamony
5
为什么在测试不等式时需要使用[[符号? - PesaThe
3
在Bash中,如果你不在第一个变量周围加上引号,就会产生"[: ==: unary operator expected"的错误。所以要改成if [ "${file: -4}" == ".txt" ] - Giles B
1
对于那些可能不熟悉bash/shell语法的人来说,需要注意在-4之前的空格是有意义的。"${file:-4}" == ".txt"(没有空格)将不能按预期工作。 - KOGI
显示剩余2条评论

35

你也可以这样做:

   if [ "${FILE##*.}" = "txt" ]; then
       # operation for txt files here
   fi

16
使用case $FILE in *.txt ) ... ;; esac会使代码更加健壮和符合惯用语法。 - tripleee
2
这将适用于最基本的shell,更加通用。 - Kemin Zhou
最便携的答案。 - Daniel Roethlisberger

29

在Unix系统中,你不能确定一个.txt文件是否真的是文本文件。最好的方法是使用“file”命令。可以尝试使用:

file -ib "$file"
然后,您可以使用MIME类型列表进行匹配,或解析MIME的第一部分,其中包含诸如“文本”、“应用程序”等内容。

3
由于 file -i... 包含了 MIME 编码,因此您可以使用 file --mime-type -b ... - Wilf
我发现 file -b --mime-type 更简洁。file -b --extension 也很不错。 - Ahmad Ismail

23

如果你想真正了解文件的信息而不是依赖扩展名,可以使用 "file" 命令。

如果你对使用扩展名感到舒适,可以使用 grep 命令查看是否匹配。


是的,我知道file命令。实际上,我已经尝试根据该命令的输出进行匹配...但是我的if语句写得非常糟糕。 - theman_on_vista

17

在Linux中提取文件名中的扩展名的正确方法是:

${filename##*\.} 

打印目录中所有文件扩展名的示例

for fname in $(find . -maxdepth 1 -type f) # only regular file in the current dir
    do  echo ${fname##*\.} #print extensions 
done

你的答案使用了双斜杠,但是你的例子只使用了单斜杠。你的例子是正确的,而你的答案则不是。 - gilbertpilz
请问您能否稍微解释一下答案或提供文档链接?我需要了解${fname##*的含义。谢谢。 - thermz

12
与'file'类似,使用稍微简单的'mimetype -b',无论文件扩展名如何都可以工作。
if [ $(mimetype -b "$MyFile") == "text/plain" ]
then
  echo "this is a text file"
fi

编辑:如果mimetype不可用,则您可能需要在系统上安装libfile-mimeinfo-perl


1
你应该明确指出,“mimetype”脚本并非所有系统都可用。 - gilbertpilz
完成,已添加 libfile-mimeinfo-perl。 - dargaud

3

我猜'$PATH_TO_SOMEWHERE'是类似于'<directory>/*'的东西。

在这种情况下,我会更改代码为:

find <directory> -maxdepth 1 -type d -exec ... \;
find <directory> -maxdepth 1 -type f -name "*.txt" -exec ... \;

如果你想要对目录和文本文件名进行更加复杂的操作,可以尝试以下方法:

find <directory> -maxdepth 1 -type d | while read dir; do echo $dir; ...; done
find <directory> -maxdepth 1 -type f -name "*.txt" | while read txtfile; do echo $txtfile; ...; done

如果您的文件名中有空格,您可以采取以下措施:

find <directory> -maxdepth 1 -type d | xargs ...
find <directory> -maxdepth 1 -type f -name "*.txt" | xargs ...

这些是如何在shell中执行“循环”的绝佳示例。当循环体需要更复杂的操作时,最好保留显式的forwhile循环。 - user49586

3
我写了一个bash脚本,它可以检查文件类型并将其复制到指定位置。我用它来查找我从Firefox缓存中观看的在线视频。
#!/bin/bash
# flvcache script

CACHE=~/.mozilla/firefox/xxxxxxxx.default/Cache
OUTPUTDIR=~/Videos/flvs
MINFILESIZE=2M

for f in `find $CACHE -size +$MINFILESIZE`
do
    a=$(file $f | cut -f2 -d ' ')
    o=$(basename $f)
    if [ "$a" = "Macromedia" ]
        then
            cp "$f" "$OUTPUTDIR/$o"
    fi
done

nautilus  "$OUTPUTDIR"&

它使用了类似于这里提出的思想,希望对某些人有所帮助。


3

感谢Jox提供了大部分的答案,不过我发现(js)会匹配.json文件,所以我添加了一个行尾字符以更完全地匹配扩展名。

$file不需要用引号括起来,因为[[ ]]不会扩展变量,所以空格不是问题(参考:Hontvári Levente

if [[ $file =~ .*\.(js$|json$) ]]; then
  echo "The extension of '$file' matches .js|.json";
fi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接