如何以编程方式判断文件名是否与Shell Glob模式匹配?


13

我想告诉您一个字符串$string是否可以通过glob模式进行匹配$pattern$string可能是也可能不是现有文件的名称。我怎样才能做到这一点?

假设我的输入字符串采用以下格式:

string="/foo/bar"
pattern1="/foo/*"
pattern2="/foo/{bar,baz}"

我想找到一个bash成语,决定是否$string将被匹配$pattern1$pattern2或任何其它任意的glob模式。到目前为止,这是我尝试过的:

  1. [[ "$string" = $pattern ]]

    除了$pattern被解释为字符串模式而不是全局模式之外,这几乎可以工作。

  2. [ "$string" = $pattern ]

    这种方法的问题$pattern是先进行扩展,然后在之间进行字符串比较,然后$string对进行扩展$pattern

  3. [[ "$(find $pattern -print0 -maxdepth 0 2>/dev/null)" =~ "$string" ]]

    $string文件有效,但前提是包含一个存在的文件。

  4. [[ $string =~ $pattern ]]

    这不起作用,因为=~运算符导致$pattern将其解释为扩展的正则表达式,而不是全局或通配符模式。


2
您将遇到的问题{bar,baz}不是模式。它是参数扩展。细微但关键的区别{bar,baz}很早就扩展为多个论点,bar并且baz
Patrick

如果外壳程序可以扩展参数,则可以肯定地告诉它字符串是否是glob的潜在扩展。
jayhendren 2014年

尝试一下= ls /foo/* 现在您可以参加
Hackaholic

1
@Patrick:在阅读了bash手册页之后,我了解到foo/{bar,baz}实际上是大括号扩展(不是参数扩展),而foo/*实际上是路径名扩展。 $string是参数扩展。这些都是在不同的时间和通过不同的机制完成的。
jayhendren 2014年

@jayhendren @Patrick是正确的,然后您了解到您的问题最终不是标题使人相信的问题。相反,您想将字符串与各种模式匹配。如果要严格匹配全局模式,则该case语句按照Bash手册执行Pathname Expansion(“ globbing”)。
Mike S

Answers:


8

对于此问题没有通用的解决方案。原因是,在bash中,花括号扩展(即,{pattern1,pattern2,...}文件名扩展(又称为glob模式))被视为单独的事物,并且在不同的条件和时间下进行了扩展。以下是bash执行的扩展的完整列表:

  • 大括号扩展
  • 波浪线扩展
  • 参数和变量展开
  • 命令替换
  • 算术展开
  • 分词
  • 路径扩展

由于我们只关心其中的一个子集(也许是大括号,代字号和路径名扩展),因此可以使用某些模式和机制以可控的方式限制扩展。例如:

#!/bin/bash
set -f

string=/foo/bar

for pattern in /foo/{*,foo*,bar*,**,**/*}; do
    [[ $string == $pattern ]] && echo "$pattern matches $string"
done

运行此脚本将生成以下输出:

/foo/* matches /foo/bar
/foo/bar* matches /foo/bar
/foo/** matches /foo/bar

这是set -f可行的,因为禁用了路径名扩展,因此语句中仅出现大括号扩展和代字号扩展for pattern in /foo/{*,foo*,bar*,**,**/*}。然后,[[ $string == $pattern ]]在执行了括号扩展之后,我们可以使用测试操作 对路径名扩展进行测试。


7

我不相信这{bar,baz} 一个shell全局模式(尽管可以肯定/foo/ba[rz]),但是如果您想知道是否$string匹配$pattern,可以这样做:

case "$string" in 
($pattern) put your successful execution statement here;;
(*)        this is where your failure case should be   ;;
esac

您可以根据需要执行任意操作:

case "$string" in
($pattern1) do something;;
($pattern2) do differently;;
(*)         still no match;;
esac

1
嗨,@ mikeserv,正如我在上面的评论和答案中所指出的那样,我已经了解到您所说的是正确的- {bar,baz}不是全局模式。我已经想出了一个解决这个问题的解决方案。
jayhendren 2014年

3
+1这将完全按照标题和第一句中的答案回答问题。尽管这个问题后来用shell glob模式将其他模式混为一谈。
Mike S

3

正如Patrick所指出的,您需要一种“不同类型”的模式:

[[ /foo/bar == /foo/@(bar|baz) ]]


string="/foo/bar"
pattern="/foo/@(bar|baz)"
[[ $string == $pattern ]]

那里没有引号。


好的,这可行,但是严格来说,它不能回答我的问题。例如,我想考虑来自其他来源的模式,即这些模式不在我的控制范围内。
jayhendren 2014年

@jayhendren然后,您可能必须首先将传入的模式转换为那些bash接受的模式。
Hauke Laging

因此,您真正完成的所有工作将我的问题从“如何确定文件名是否可能扩展表达式”转变为“如何将普通的bash样式文件名模式转换为bash样式扩展的glob模式”。
jayhendren

@jayhendren考虑到您想要的东西似乎“不可能”已经对我听起来有点奇怪,但这也许只是一门外语。如果您想问这个新问题,那么您必须说明输入模式是什么样的。也许这是一个简单的sed操作。
Hauke Laging

1
@HaukeLaging是正确的。像我一样,来这里弄清楚如何与glob模式匹配(又称“路径名扩展”)的人很容易感到困惑,因为标题说的是“ shell glob pattern”,但他的问题内容使用了非glob模式。在某种程度上,贾森德伦了解了这种区别,但是他最初的困惑是促使豪克回答他的方式的原因。
Mike S

1

我将因此使用grep:

#!/bin/bash
string="/foo/bar"
pattern1="/foo/*"
pattern2="/foo/{bar,baz}"

if echo $string | grep -e "$pattern1" > /dev/null; then
    echo $string matches $pattern1
fi

if echo $string | grep -e "$pattern2" > /dev/null; then
    echo $string matches $pattern2
fi

给出输出:

./test2.bsh 
/foo/bar matches /foo/*

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.