Оболочка, совпадающая с шаблоном с использованием оператора case, который хранится в переменной - PullRequest
7 голосов
/ 13 октября 2009

Я пытаюсь сопоставить шаблон с оператором case, в котором шаблон хранится внутри переменной. Вот минимальный пример:

PATTERN="foo|bar|baz|bla"

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

К сожалению, "|" кажется, сбежал (интересно, «*» или «?» нет). Как мне заставить это работать, то есть соответствовать "foo"? Мне нужно хранить шаблон в переменной, потому что он создается динамически. Это должно работать на POSIX-совместимой оболочке.

Ответы [ 7 ]

3 голосов
/ 13 октября 2009

Это должно работать:

PATTERN="foo|bar|baz|bla"

shopt -s extglob

case "foo" in
    @($(echo $PATTERN)))
        printf "matched\n"
        ;;
    *)
        printf "no match\n"
        ;;
esac
1 голос
/ 13 октября 2009

Ваш шаблон на самом деле представляет собой список шаблонов, и разделитель | должен быть задан буквально. Ваш единственный вариант, кажется, eval. Однако старайтесь избегать этого, если сможете.

0 голосов
/ 11 мая 2017

Можно сопоставить подстроку в строке, не вызывая подпроцесс (такой как grep), используя только POSIX-совместимые методы sh(1p), как определено в Раздел 2.6.2, Расширение параметров .

Вот удобная функция:

# 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, но я считаю, что это для другого ответа.

0 голосов
/ 10 декабря 2011

Это устраняет необходимость побега или что-то еще:

PATTERN="foo bar baz bla"

case "foo" in
    ${PATTERN// /|})
        printf "matched\n"
        ;;
    *)
        printf "no match\n"
        ;;
esac
0 голосов
/ 06 ноября 2009

"Вы не можете добраться отсюда"

Мне нравится использовать case для сопоставления с образцом, но в этой ситуации вы пересекаете границы того, для чего подходит оболочка Bourne.

есть два способа решения этой проблемы:

за счет вилки, вы можете использовать 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

Это явно шоу ужасов, пример того, как сценарии оболочки могут быстро выйти из-под контроля, если вы попытаетесь заставить что-либо сделать хоть что-то немного не по карте.

0 голосов
/ 13 октября 2009

Вы можете использовать регулярное выражение в Bash:

PATTERN="foo|bar|baz|bla"

if [[ "foo" =~ $PATTERN ]]
then
    printf "matched\n"
elif . . .
    . . .
elif . . .
    . . .
else
    printf "no match\n"
fi
0 голосов
/ 13 октября 2009

Некоторые версии 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
...