2

Я хотел бы иметь что-то вроде следующего:

#!/bin/sh
# ... other stuff ...
# some relatively static possibilities (srsp):
srsp='this|or|that|the|other'
# more other stuff
case $something in
    $srsp) # <- problem is here
        do_something # or maybe nothing
        ;;
    this|or|that|the|other);; # this would work, but loses the benefit of a variable
    *)
        # anything not in my list is an error:
        echo "Sorry, I don't recognize $something as one of $srsp" >&2
        exit 1;;
esac

do_something | egrep "blah($srsp)thing" # or whatever

Проблема в том, что наличие $srsp только всей строке (если бы $something было в точности строкой "this|or|that|or|some|other|stuff" , это совпадало бы и do_something), а не что-либо из this или or или that ... и так далее: значения, которые я на самом деле хочу сопоставить.

Если я добавлю эту буквальную строку в оператор case (моя строка "это будет работать"), она будет соответствовать тому, что я хочу, чтобы она соответствовала, но я пытаюсь сохранить этот СУХОЙ, и мне нужен тот же набор строк в обычном выражение я использую позже. (Примечание: я понимаю, что то, что возможно в case совпадения, и то, что возможно в регулярном выражении, может существенно отличаться, но в моей конкретной ситуации они совместимы с обоими. Это на самом деле просто буквы в отдельных компонентах, без подстановочных знаков, просто | , который существует как частный случай в обеих системах.)

Так есть ли способ сделать это? Особенно без упаковки всего выражения case в eval или что-то еще? Я бы хотел сохранить это в заявлении по делу, потому что у меня есть другие дела. (Я уверен, что я мог бы реализовать обходной путь, изменив структуру и используя egrep в качестве моего теста на совпадение (if echo $something | egrep "^($srsp)$" > /dev/null или что-то подобное). Этот вопрос о попытке найти способ сделать это, не прибегая к этому. Или окончательное знание, что это не может быть сделано, также будет правильным ответом.)

(Или я должен переключиться на Common-Lisp? ;))

Для моих нужд я был бы доволен sh или bash наверняка, и, возможно, zsh , хотя, если бы был способ сделать это максимально переносимым способом (то есть sh), это помогло бы получить лучший ответ, ИМХО.

2 ответа2

2

Это на самом деле не касается case как такового: вам нужен механизм, чтобы проверить, присутствует ли слово в списке. С Bash вы можете сделать это:

srsp=(this that or the other)
in_srsp() {
    # this joins the array into a string with spaces (default value of IFS)
    # then tests if the space-delimited word is a match for that string.
    [[ " ${srsp[*]} " == *" $1 "* ]]
}
something=foo
if in_srsp $something; then echo y; else echo n; fi   # =>  n
something=other
if in_srsp $something; then echo y; else echo n; fi   # =>  y

Точнее просто перевернуть дело

srsp="this|that|or|the|other"
something=other
case "|$srsp|" in
    *"|$something|"*) echo in;;
    *) echo not;;
esac
1

Конечно, вы можете использовать eval , но это довольно громоздко. Возможно, заключите его в функцию, чтобы избежать слишком большого количества обратных косых черт за пределами действительно сложного eval .

in_list () {
    local var
    var=$1
    local list
    list=$2
    eval case \$var in "$list"\) return 0\;\; \*) return 1\;\; esac
}

in_list "foo" "foo|bar|baz" && echo Success
in_list "nonesvch" "foo|bar|baz" || echo More so

Если вы знаете, что можете ограничить набор символов в списке, возможно, запустите case фильтрации до eval безопасности, что-то вроде

in_list () {
    local var
    var=$1
    local list
    list=$2

    case $var in *[!-a-z0-9_]*) return 1;; esac

    # Additionally, allow wildcards and whitespace in list
    case $list in *[!?*| a-z0-9_-]*)
        echo "$0: Invalid characters in $list" >&2; return 2;;
    esac

    eval case \$var in "$list"\) return 0\;\; \*) return 1\;\; esac
}

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .