如何在不关心区分大小写的情况下遍历多个文件扩展名?

mrj*_*per 2 scripting bash shell-script

我已经看到有关如何遍历多个文件扩展名的各种主题,但在大多数情况下,列表已定义。

例子:

for file in ${arg}/**/*.{txt,h,py}
do
    ....
done
Run Code Online (Sandbox Code Playgroud)

可以看出,.TXT文件将被忽略。可悲的是,回答它的人说它只适用于 bash4。但我的设置使用 bash3.x。

关于如何做到这一点的任何想法?

Gra*_*eme 7

bash你可以设置nocaseglob

shopt -s nocaseglob

for file in "$arg"/**/*.{txt,h,py}
do
  ....
done

shopt -u nocaseglob
Run Code Online (Sandbox Code Playgroud)

noclaseglob可以在bash2.01以后的任何版本中使用,但是**需要bash4.0 或更高版本(并且它遵循符号链接,直到bash4.3 已修复)。请注意对引号的更正,$arg因为如果其中包含空格或通配符,则会出现问题。

不使用**您可以执行以下操作:

find "$arg" \( -iname '*.txt' -o -iname '*.h' -o -iname '*.py' \) -exec bash -c '
  for file; do
    ...
  done' bash {} +
Run Code Online (Sandbox Code Playgroud)

This will find all the files you are looking for and pass them as separate arguments to a new bash instance, which will then loop through them. If your per file operation is a single command, you can probably skip the new bash instance and use the command directly.

Update

As per Stephane's comments below, the POSIX compatible solution is:

find "$arg" \( -name '*.[tT][xX][tT]' -o -name '*.[hH]' -o -name '*.[pP][yY]' \) \
  -exec sh -c '
  for file do
    ...
  done' sh {} +
Run Code Online (Sandbox Code Playgroud)

  • `**` 出现在 bash 4 中(从 ksh93 借来,本身借用了它,但与 zsh 有所不同)。以下符号链接已在 4.3 中修复。你需要`shopt -s globstar`。 (2认同)

Sté*_*las 7

Use zsh instead:

setopt extendedglob
for f ($arg/**/*.(#i)(txt|h|py)(N.)) {
  ...
}
Run Code Online (Sandbox Code Playgroud)
  • **/: any level of subdirectories
  • (#i) is to turn case insensitive matching on for the rest of the glob (like ~(i) in ksh93).
  • (N.) is the glob qualifier. N is to not return an error if there's no matching file and . to select regular files only (the equivalent of find's -type f)). You could add a D in there to also match dot (hidden) files and descend into hidden dirs (like find would be default).