是否有类似 tcsh 的 shopt glob 设置或设置组合?

hep*_*t72 7 bash tcsh wildcards shopt

我正在从一个长期的 tcsh 用户过渡到一个新的 bash 用户(它已经过期了)。我定期在 tcsh 中动态地编写了很多 foreach 循环,所以我学习了 bash 的 for 循环的语法作为替代,但是当不匹配的 glob 模式作为文字字符串通过循环时感到惊讶。我寻找一种方法来改变这种行为,这样文字字符串就会被跳过并找到shopt -s nullglob。我的理解是,这在理论上应该等同于 tcsh 的行为方式,但今天我发现了一个不同之处。当我这样做时ls ../*.doesnotmatch,结果是当前目录内容的列表。具体来说,我是这样做的:

重击:

$ shopt -s nullglob
$ ls ../*.sam
extractSplitReads_BwaMem    extractSplitReads_BwaMem.xml
$ shopt -u nullglob
$ ls ../*.sam
ls: ../*.sam: No such file or directory
Run Code Online (Sandbox Code Playgroud)

父目录中没有匹配的内容*.sam,尤其是当前目录。一开始我真的很困惑,但后来我意识到 glob 模式正在消失并且命令正在执行,就好像我没有提供任何参数一样,例如:

$ ls
Run Code Online (Sandbox Code Playgroud)

所以我尝试单独设置failglob和使用nullglob,但是只要设置了failglob,任何不匹配的glob模式都会终止命令,无论是否存在匹配的模式:

重击:

$ shopt -s failglob
$ shopt -s nullglob
$ ls ../vis*/*.xml
../visualization/LAJ.xml
$ ls ../vis*/*.xml ../*.sam
bash: no match: ../*.sam
$ ls ../{vis*/*.xml,*.sam}
bash: no match: ../*.sam
Run Code Online (Sandbox Code Playgroud)

当我使用 tcsh 时,所有的 glob 都归结为那些匹配的东西,如果没有匹配,你会得到一个 glob 错误:

tcsh:

$ ls ../vis*/*.xml ../*.sam
../visualization/LAJ.xml
$ ls ../{vis*/*.xml,*.sam}
../visualization/LAJ.xml
$ ls ../*.sam
ls: No match.
Run Code Online (Sandbox Code Playgroud)

我查看了 shopt 设置,但没有找到获得这种行为的方法。我错过了什么吗?除了 bash 或 tcsh 之外,还有其他现代 shell 以 tcsh 的方式处理 globs 吗?我想要匹配时 nullglob 的行为,但是当没有匹配时 failglob 的行为,这似乎是 tcsh 的工作原理。

nxn*_*nev 5

shopt与文件名扩展相关的唯一选项是dotglob、和,并且它们(单独或组合)似乎都不能完全满足您的要求failglob。真遗憾,因为这听起来确实是个好主意。nocaseglobnullglob

我的建议是在交互式会话中进行failglob设置,这样您就可以避免可能不需要的命令,例如:

mv -r file1 file2 dir1 dir2 destination-*-dir
Run Code Online (Sandbox Code Playgroud)

如果file1、和不匹配且已设置,file2则 和dir1将被移至其中。dir2destination-*-dirnullglob

另一方面,在 shell 脚本编写时完全依赖文件名扩展并不是一个好主意。建议始终验证此类扩展是否存在以及它们应该是什么。

我的意思是,不要这样做:

rm -- *.jpg *.txt
Run Code Online (Sandbox Code Playgroud)

最好做这样的事情:

for file in *.jpg *.txt; do
  if [ -f "${file}" ]; then
    rm -- "${file}"
  fi
done

# Or this (non-POSIX, as it uses an array)

for file in *.jpg *.txt; do
  if [ -f "${file}" ]; then
    files_to_delete+=( "${file}" )
  fi
done

if [ "${#files_to_delete[@]}" -gt 0 ]; then
  rm -- "${files_to_delete[@]}"
fi
Run Code Online (Sandbox Code Playgroud)

这样,即使某些文件匹配*.txt但它实际上是一个目录,您也会安全。