shell使用case语句匹配模式,case语句存储在变量中

时间:2009-10-13 17:46:07

标签: shell posix sh

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

PATTERN="foo|bar|baz|bla"

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

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

7 个答案:

答案 0 :(得分:3)

这应该有效:

PATTERN="foo|bar|baz|bla"

shopt -s extglob

case "foo" in
    @($(echo $PATTERN)))
        printf "matched\n"
        ;;
    *)
        printf "no match\n"
        ;;
esac

答案 1 :(得分:1)

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

答案 2 :(得分: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

答案 3 :(得分:0)

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

PATTERN="foo|bar|baz|bla"

if [[ "foo" =~ $PATTERN ]]
then
    printf "matched\n"
elif . . .
    . . .
elif . . .
    . . .
else
    printf "no match\n"
fi

答案 4 :(得分:0)

“你不能从这里到达那里”

我喜欢使用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脚本如何迅速失去控制的一个例子。

答案 5 :(得分:0)

可以匹配字符串中的子字符串而不使用sh(1p)中仅符合POSIX的方法生成子进程(例如grep),如{{3}中所定义}。

这是一个便利功能:

# 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

注意

有时候记住你可以在Section 2.6.2, Parameter Expansion(也是POSIX)中完成整个脚本是件好事,但我相信这是另一个答案。

答案 6 :(得分:-1)

这消除了逃避或其他任何事情的需要:

PATTERN="foo bar baz bla"

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