bash - bash 如何处理不匹配的通配符?

标签 bash wildcard

我学习了 bash,使用通配符遇到了下一个问题:

sergs:~ > ls -1
a.sh
Desktop
Downloads
eclipse
sergs:~ > a=*.sh
sergs:~ > echo $a
a.sh
sergs:~ > a=*.sha
sergs:~ > echo $a
*.sha

如果没有文件与 *.sha 模式匹配,为什么 bash 返回模式本身而不是不返回任何内容?

因此行为打破了这样的逻辑:

for i in /usr/lib/opkg/info/*.postinst; do do_some_logic_with_postinst_file; done

我是否必须在循环之前明确检查 /usr/lib/opkg/info/ 目录是否没有任何 *.postinst 文件,或者有一些其他方法来做到这一点。

最佳答案

除非您打开扩展 shell 选项 nullglob,否则在命令行中按字面返回未扩展的 glob 字符串,该选项在 glob 扩展失败时不返回任何结果。

对于您的用例来说,通过测试扩展结果是否是一个文件来检查 for 循环中的 glob 扩展是否成功始终是一个好习惯,即

for file in /usr/lib/opkg/info/*.postinst; do 
    [ -f "$file" ] || continue
    do_some_logic_with_postinst_file
done

条件 [ -f "$file"] 将检查您的 glob 扩展,即是否有任何 .postinst 文件 [ -f "$file"] 为真。对于未扩展的 glob,条件

[ -f /usr/lib/opkg/info/*.postinst ] 

将失败,因为文字字符串 /usr/lib/opkg/info/*.postinst 不是有效文件,并且此 test 命令失败 bool OR || 导致 for 循环执行 continue 操作。由于 for 循环不再需要处理任何参数,因此循环会正常退出。

这样做(使用 -f 针对 glob 字符串)可以保证符合 POSIX 标准。不过,对于 bash 特定答案,您可以打开前面提到的选项

# '-s' enables the option
shopt -s nullglob

for file in /usr/lib/opkg/info/*.postinst; do 
    do_some_logic_with_postinst_file
done

# '-u' disables the option
shopt -u nullglob

一旦您决定使用 nullglob 选项,您可以通过多种方式使用它来运行您的功能。例如您可以将所有 glob 结果放入数组类型中并检查长度是否有效

shopt -s nullglob
results=(/usr/lib/opkg/info/*.postinst)

if (( ${#results[@]} )); then
    printf '%s\n' 'my array is not empty'
    for file in "${results[@]}"; do
        printf '%s\n' "some action on ${file}"
    done
fi

shopt -u nullglob

不使用 nullglob 选项时请小心这种方法。如果没有相同的内容,results=(/usr/lib/opkg/info/*.postinst) 部分仍然可能是非空的,因为文字字符串仍然存储在数组中。

你的OP的另一个小附录是永远不要使用变量来保存你的glob字符串。因为当变量不带引号展开时,如果全局展开成功,则变量的内容会受到 shell 完成的分词的影响,并且包含空格的文件名可能会在您希望不分解时分解为多个条目。

始终使用数组和正确的带引号的扩展。查看更多 BashFAQ - BashGuide/Arrays有关该主题的精彩读物。

关于bash - bash 如何处理不匹配的通配符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57353542/

相关文章:

java - 避免返回通配符类型的方法

linux - 如何在 Unix 中创建附加到文件的 Shell 脚本?

bash - 将多行分隔子字符串读取到 bash 数组

linux - 使用 bash 脚本在 gedit 中预定义的文本

php - mod_rewrite 能否在 URL 中找到一个变量,替换它之前的部分,删除变量并保留剩下的部分?

php - 当某些输入参数使用值 'all' 作为通配符时,如何编写 PHP 代码(使用准备好的语句)从 MySQL DB 返回行?

Bash 如何获取字符串的倒数第二个字符?

bash - 带有 wget 的 Shell 脚本获取减去 1 天的日期

list - 过滤我自己类型的列表 - 元组?

java - 避免返回通配符类型