我正在从长期的 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
父目录中没有任何内容与 匹配*.sam
,特别是当前目录。一开始我真的很困惑,但后来我意识到全局模式正在消失,并且命令正在执行,就好像我没有提供任何参数一样,例如:
$ ls
因此,我尝试单独设置failglob以及使用nullglob设置failglob,但只要设置了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
当我使用 tcsh 时,所有的 glob 都被归结为那些匹配的东西,如果没有匹配的东西,你会得到一个 glob 错误:
tcsh:
$ ls ../vis*/*.xml ../*.sam
../visualization/LAJ.xml
$ ls ../{vis*/*.xml,*.sam}
../visualization/LAJ.xml
$ ls ../*.sam
ls: No match.
我查看了 shopt 设置,但没有找到实现此行为的方法。我错过了什么吗?除了 bash 或 tcsh 之外,是否还有另一种现代 shell 可以像 tcsh 一样处理全局变量?我想要当有匹配的时候 nullglob 的行为,但是当没有匹配的时候failglob 的行为,这似乎是 tcsh 的工作方式。
答案1
shopt
与文件名扩展相关的唯一选项是dotglob
、failglob
和,并且它们(单独或组合)似乎都不能完全满足您的要求。真遗憾,因为这听起来确实是个好主意。nocaseglob
nullglob
我的建议是failglob
在交互式会话中进行设置,这样您就可以避免可能不需要的命令,例如:
mv -r file1 file2 dir1 dir2 destination-*-dir
如果file1
、和不匹配且已设置,file2
则 和dir1
将被移至其中。dir2
destination-*-dir
nullglob
另一方面,在 shell 脚本编写时完全依赖文件名扩展并不是一个好主意。建议始终验证此类扩展是否存在以及它们应该是什么。
我的意思是,不要这样做:
rm -- *.jpg *.txt
最好做这样的事情:
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
这样,即使某些文件匹配*.txt
但它实际上是一个目录,您也会安全。