Bash 通配符匹配除具有特定扩展名的文件之外的所有文件,适用于包含点字符的文件名

Bash 通配符匹配除具有特定扩展名的文件之外的所有文件,适用于包含点字符的文件名

我正在用音频文件做一些事情,其中​​大部分但不是全部是 mp3 文件。现在我只想对非 mp3 文件或没有扩展名的文件运行一些命令.mp3

我认为自己非常擅长正则表达式,但不太擅长文件通配,这在意想不到的方面有微妙的不同。

我环顾四周,从其他 SO & SE 答案中了解到,Bash 具有“扩展的通配符”,允许我这样做:

file ../foo/bar/*.!(mp3)

但我的一些文件名除了形成文件​​扩展名的点之外还包含点:

../foo/bar/Naked_Scientists_Show_19.10.15.mp3
../foo/bar/YWCS_ep504-111519-pt1_5ej4_41cc9320.mp3_42827d48daefaa81ec09202e67fa8461_24419113.mp3
../foo/bar/eLife_Podcast_19.09.26.mp3
../foo/bar/gdn.sci.080428.bg.science_weekly.mp3

看起来全局从第一个点开始匹配,而不是从最后一个点开始匹配。我查看了文档,但似乎它们远不如正则表达式强大。但我并没有真正理解所有内容,因为我没有花太多时间在 *nix shell 上。

我是否错过了一些仍然可以通过 Bash 通配符来做到这一点的方法?如果没有,使用 find 或其他工具实现相同目的的方法仍然值得了解。

答案1

*.!(mp3)匹配 onfoo.bar.mp3因为 thatfoo.后面跟着bar.mp3is不是mp3

您想要!(*.mp3)这里,它匹配任何不以 结尾的内容.mp3

如果您想匹配名称中至少包含一个.(除了会使它们成为隐藏文件的前导文件)但不以 结尾的文件.mp3,您可以这样做!(*.mp3|!(*.*))

无论如何,请注意,除非您bash是使用 构建的--enable-extended-glob-default,否则您需要shopt -s extglob使 ksh glob 运算符可用。

答案2

还有变量GLOBIGNORE

shellGLOBIGNORE变量可用于限制与模式匹配的文件名集。如果GLOBIGNORE设置,GLOBIGNORE则将从匹配列表中删除也与其中模式之一匹配的每个匹配文件名。如果nocaseglob设置了该选项,则执行与 中的模式的匹配GLOBIGNORE,而不考虑大小写。当设置且不为空时,文件名...始终被忽略 。GLOBIGNORE但是,设置GLOBIGNORE为非空值具有启用dotglobshell 选项的效果,因此以 ' ' 开头的所有其他文件名.都将匹配。要获得忽略以 ' ' 开头的文件名的旧行为.,请将 ' .*' 设为GLOBIGNORE.未设置时该dotglob选项将被禁用。GLOBIGNORE

$ touch a.b.mp3 .c.mp3 foo.tgz .bar
$ echo *
a.b.mp3 foo.tgz
$ shopt -s extglob; echo *.!(mp3)
a.b.mp3 foo.tgz
$ GLOBIGNORE='*.mp3'; echo *
.bar foo.tgz

答案3

我能想到的最简单的方法:

find ${path-to-folder} -type f  | grep -vE ".*\.mp3$"

您可以找到文件夹中的所有文件并将其通过管道传输到反向扩展 grep:

查找某个位置的所有文件

find ${path-to-folder} -type f 

反向 grep 并使用正则表达式按扩展名进行过滤

grep -vE ".*\.mp3$"

Grep 标志:

  • -v反向grep

  • -E扩展 grep(使用正则表达式)

正则表达式解释:

  • .*表示从 0 到任何字符数,任何可能的字符

  • \.寻找实际的点

  • mp3对于形成扩展名的字符串

  • $代表停产

答案4

可能不适合你的情况,但我想知道如何用纯函数创建一个函数Bash,然后将扩展名作为参数传递,如果你想将它添加到你的bashrc并且你想在将来做同样的事情,这会很方便,像下面这样

所以,基本上我们正在做的是正则表达式检查您想要的扩展。

for file in *; do
  if [[ $file =~ \.mp3$ ]]; then
    echo $file
  fi
done

在函数中实现它:这将输出当前目录中具有特定扩展名的所有文件。

getFileByExt(){
local extension="$1"
if (( $# == 1 )); then
for file in *; do
  if [[ $file =~ \.$extension$ ]]; then
    echo $file
  fi
done
else
  echo "Usage: ${FUNCNAME[0]} <extension>" 
## If run from bashrc, return the name of function
fi
}

现在,例如要获取所有具有扩展名的文件mp3,只需执行以下操作即可简单:

getFileByExt mp3

并获取没有特定扩展名的文件:这将输出当前目录中没有特定扩展名的所有文件。

getFileWithoutExt(){
local extension="$1"
if (( $# == 1 )); then
for file in *; do
  if [[ ! $file =~ \.$extension$ ]]; then
    echo $file
  fi
done
else
  echo "Usage: ${FUNCNAME[0]} <extension>"
fi
}

类似地,要获取没有mp3扩展名的文件,只需执行以下操作:

getFileWithoutExt mp3

欲了解更多信息:

https://stackoverflow.com/questions/407184/how-to-check-the-extension-of-a-filename-in-a-bash-script

https://stackoverflow.com/questions/18278990/easiest-way-to-check-for-file-extension-in-bash/18280350

相关内容