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


169

我正在用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

Answers:


248

我想您想说“ $ file的最后四个字符等于.txt吗?” 如果是这样,您可以使用以下方法:

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

需要注意的是之间的空间file:-4需要,为“: - ”修饰手段不同的东西。


1
为此,您也可以在Windows计算机上将command.com重命名为command.txt。
hometoast

9
如果要指定不等式,请记住包括其他括号:if [[$ {file:-4}!=“ .txt”]]
Ram Rajamony

4
@RamRajamony为什么在测试不平等时需要使用[[?
PesaThe

我想指出,冒号后面的空间很重要。${var:-4}与...不同${var: -4}; 如果未设置var,则第一个(无空格)将扩展为'-4';第二个(无空格)将返回var的最后4个字符。
pbatey

1
在bash中,除非您在第一个变量周围加上引号,否则将产生“ [:==:一元运算符期望”错误。因此,if [ "${file: -4}" == ".txt" ]相反。
Giles B

264

使

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

像这样:

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

也就是说,双括号中没有引号。

的右侧==是贝壳图案。如果需要正则表达式,请使用=~then。


15
我对此一无所知。似乎很特殊的情况是==或!=的右侧被扩展为外壳模式。我个人认为这比我的答案清楚。
Paul Stephenson 2009年

20
我是bash的新手,花了我一段时间才弄清楚如何在多条件if语句中使用它。我在这里分享它是为了帮助别人。if [[ ( $file == *.csv ) || ( $file == *.png ) ]]
joelostblom 2015年

6
@cheflo通常适用于多种情况。在这种情况下,您也可以使用if [[ $file =~ .*\.(csv|png) ]]。它更短,更清晰,更容易添加其他扩展名,并且可以很容易地进行配置(通过在变量中添加“ csv | png”)。
jox

4
您可以在文件两边加上双引号。if [[ "$file" == *.txt ]]如果文件名中包含空格,则需要双引号。
shawnhcorey

我应该用这个答案代替被接受的答案吗?
Freedo

26

您只是不确定在Unix系统上,.txt文件确实是文本文件。最好的选择是使用“文件”。也许尝试使用:

file -ib "$file"

然后,您可以使用MIME类型的列表来匹配或解析MIME的第一部分,在该部分中您会看到诸如“文本”,“应用程序”等内容。


2
由于file -i...包括MIME编码,您可以使用file --mime-type -b ...
维尔夫

24

如果您实际上想查找有关文件的信息,而不是依赖扩展名,则可以使用“文件”命令。

如果您对使用扩展名感到满意,可以使用grep查看它是否匹配。


是的,我知道该file命令。我实际上已经尝试根据所述命令的输出进行匹配...但是在这些if语句上我却失败了。

17

您也可以这样做:

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

11
case $FILE in *.txt ) ... ;; esac看起来更加健壮和惯用。
Tripleee

11

与'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

5

关于如何在Linux中使用文件名中的扩展名的正确答案是:

${filename##*\.} 

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

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

您的答案使用双反斜杠,但您的示例仅使用单个反斜杠。你的例子是正确的,答案不是。
gilbertpilz

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"&

它使用与此处介绍的想法类似的想法,希望这对某人有帮助。


2

我猜'$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循环。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.