【问题标题】:How to use fnmatch from a shell?如何在 shell 中使用 fnmatch?
【发布时间】:2015-02-25 07:57:33
【问题描述】:

在通用 shell 脚本中,我想使用 shell 模式匹配来过滤文本文件的行。

我在 files.txt 中有一个文件名列表:

file1.txt
file2.sh
file3.png

我在 patterns.txt 中有一个模式列表:

other_file.txt
file2.*

如果我在 patterns.txt 中有正则表达式,我可以这样做:

$ grep -v -f patterns.txt files.txt

但我想使用 shell 通配模式。我找到了 C 函数 fnmatch 但没有使用它的 shell/unix 命令。

【问题讨论】:

  • 这可以在 shell 中完成,但它会 slooow。我建议您使用其他语言。
  • 你能简单地将你的shell表达式翻译成正则表达式吗?甚至自动化这个翻译的基本版本也可能比替代方案更好。
  • 我希望我只是错过了一个像 grep 这样的命令,它适用于 glob。翻译模式也并非易事。
  • @mirabilos 它应该在大多数 Linux 发行版 (/bin/sh)、BSD、MacOS、cygwin 和 msysgit 下运行。符合 POSIX 标准会很棒。

标签: shell glob


【解决方案1】:

好的,这将是非常糟糕的,因为 POSIX sh 甚至没有数组(我会用它来缓存模式):

while IFS= read -r filename; do
    hasmatch=0
    while IFS= read -r pattern; do
        case $filename in ($pattern) hasmatch=1; break ;; esac
    done <patterns.txt
    test $hasmatch = 1 || printf '%s\n' "$filename"
done <files.txt

如果您不需要位置参数($1$2、...),您可以滥用它们进行模式缓存:

saveIFS=$IFS; IFS='
'; set -o noglob
set -- $(cat patterns.txt)
IFS=$saveIFS; set +o noglob
while IFS= read -r filename; do
    hasmatch=0
    for pattern in "$@"; do
        case $filename in ($pattern) hasmatch=1; break ;; esac
    done
    test $hasmatch = 1 || printf '%s\n' "$filename"
done <files.txt

但请注意此处的空格:我们将 IFS 设置为文字换行符,仅此而已,即 IFS='Enter'

我已经使用您的数据集和一些附加功能(例如 a b* 模式,以测试空白行为)对此进行了测试,根据 OP 中的规范,它似乎对我有用。

【讨论】:

  • 你可以写for pattern而不是for pattern in "$@"
  • @AlexShpilkin 仅当您在 pattern 之后有一个换行符而不是分号,因为它会被某些 shell 错误解析(参见 GNU Autoconf 手册,Portable Shell 部分),而且它的风格也很糟糕(旧并且不一致),因此不推荐用于现代外壳(除了由于上述原因是不好的建议)。 – 全面披露:我是那些现代贝壳的作者之一。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-15
  • 2012-04-20
  • 2014-09-24
  • 2017-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多