10

我正在尝试将模式与存储在变量中的 case 语句匹配。这是一个最小的例子:

PATTERN="foo|bar|baz|bla"

case "foo" in
    ${PATTERN})
        printf "matched\n"
        ;;
    *)
        printf "no match\n"
        ;;
esac

不幸的是“|” 似乎被转义了(有趣的是“*”或“?”不是)。我如何让它工作,即匹配“foo”?我需要将模式存储在一个变量中,因为它是动态构造的。这需要在与 POSIX 兼容的 shell 上工作。

4

7 回答 7

5

可以使用第 2.6.2 节,参数扩展中定义grep的唯一符合 POSIX 的方法来匹配字符串中的子字符串,而无需生成子进程(例如) 。sh(1p)

这是一个便利功能:

# Note:
# Unlike a regular expression, the separator *must* enclose the pattern;
# and it could be a multi chars.

isin() {
    PATTERN=${2:?a pattern is required}
    SEP=${3:-|}
    [ -z "${PATTERN##*${SEP}${1}${SEP}*}" ]
}

例子:

for needle in foo bar; do
    isin "$needle" "|hello|world|foo|" && echo "found: $needle"
done

# using ";" as separator
for needle in foo bar; do
    isin "$needle" ";hello;world;foo;" \; && echo "found: $needle"
done

# using the string "RS" as separator
for needle in foo bar; do
    isin "$needle" "RShelloRSworldRSfooRS" RS && echo "found: $needle"
done

case如果您想要两个世界,您可以将此解决方案与语句混合使用:

PATTERN="|foo bar|baz|bla|"

case "$needle" in
    xyz) echo "matched in a static part" ;;
    *)
        if [ -z "${PATTERN##*|${needle}|*}" ]; then
            echo "$needle matched $PATTERN"
        else
            echo "not found"
        fi
esac

笔记

有时最好记住您可以在awk(1p)POSIX 中编写整个脚本,但我相信这是另一个答案。

于 2017-05-11T10:16:51.190 回答
3

这应该有效:

PATTERN="foo|bar|baz|bla"

shopt -s extglob

case "foo" in
    @($(echo $PATTERN)))
        printf "matched\n"
        ;;
    *)
        printf "no match\n"
        ;;
esac
于 2009-10-13T19:14:29.887 回答
1

“你不能从这里到那里”

我喜欢使用 case 进行模式匹配,但在这种情况下,你已经超出了 bourne shell 的优势。

有两个技巧可以解决这个问题:

以叉子为代价,您可以使用 egrep

pattern="this|that|those"

if
  echo "foo" | egrep "$pattern"  > /dev/null 2>&1
then
  echo "found"
else
  echo "not found"
fi

您也可以使用循环仅使用内置函数来执行此操作。根据具体情况,这可能会使您的代码运行速度慢十亿倍,因此请确保您了解代码的运行情况。

pattern="this|that|those"

IFS="|" temp_pattern="$pattern"
echo=echo

for value in $temp_pattern
do
  case foo 
  in
    "$list") echo "matched" ; echo=: ; break ;;
  esac
done
$echo not matched

这显然是一个恐怖节目,一个例子说明如果你试图做任何事情,即使是一点点偏离地图,shell 脚本也会迅速失控。

于 2009-11-06T17:37:57.803 回答
1

您的模式实际上是一个模式列表,分隔符|必须按字面意思给出。您唯一的选择似乎是eval. 但是,如果可以的话,尽量避免这种情况。

于 2009-10-13T18:06:31.070 回答
0

您可以在 Bash 中使用正则表达式匹配:

PATTERN="foo|bar|baz|bla"

if [[ "foo" =~ $PATTERN ]]
then
    printf "matched\n"
elif . . .
    . . .
elif . . .
    . . .
else
    printf "no match\n"
fi
于 2009-10-13T19:18:19.833 回答
0

某些版本expr(例如 GNU)允许交替进行模式匹配。

PATTERN="foo\|bar\|baz"
VALUE="bar"
expr "$VALUE" : "$PATTERN" && echo match || echo no match

否则,我会使用 awk 之类的工具:

awk -v value="foo" -v pattern="$PATTERN" '
    BEGIN {
        if (value ~ pattern) {
            exit 0
        } else {
            exit 1
        }
    }'

或更简洁地说:

awk -v v="foo" -v p="$PATTERN" 'BEGIN {exit !(v~p)}'

你可以像这样使用它:

PATTERN="foo|bar|baz"
VALUE=oops
matches() { awk -v v="$1" -v p="$2" 'BEGIN {exit !(v~p)}'; }
if matches "$VALUE" "$PATTERN"; then
    echo match
else
    echo no match
fi
于 2009-10-13T18:52:05.253 回答
-1

这消除了转义或其他任何东西的需要:

PATTERN="foo bar baz bla"

case "foo" in
    ${PATTERN// /|})
        printf "matched\n"
        ;;
    *)
        printf "no match\n"
        ;;
esac
于 2011-12-10T16:53:19.637 回答