Existe uma maneira de ter sh / bash / zsh “case” usar uma variável para um valor de teste?

2

Gostaria de ter algo como o seguinte:

#!/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

O problema é que ter $srsp corresponde apenas a string inteira (se $something fosse exatamente a string "this|or|that|or|some|other|stuff" , corresponderia e chamaria do_something ), em vez de this ou or ou that ... e assim por diante: os valores que realmente quero corresponder.

Se eu colocar essa string literal na instrução case (minha linha "isso funcionaria"), ela corresponderá ao que eu quero que corresponda, mas estou tentando manter isso DRY , e eu preciso do mesmo conjunto de strings na expressão regular que uso depois. (Nota: Eu entendo que o que é possível em uma correspondência case e o que é possível em uma expressão regular pode diferir dramaticamente, mas na minha situação particular eles são compatíveis com ambos. É realmente apenas letras nos componentes individuais, não curingas, apenas o | , que existe como um caso especial em ambos os sistemas.)

Então, existe uma maneira de fazer isso? Especialmente sem envolver toda a declaração de caso dentro de um eval ou algo assim? Eu gostaria de mantê-lo em uma declaração de caso, porque eu tenho outras coisas acontecendo. (Tenho certeza de que poderia implementar uma solução alternativa reestruturando e usando o egrep como meu teste de correspondência ( if echo $something | egrep "^($srsp)$" > /dev/null , ou algo assim). Essa pergunta é sobre como tentar encontrar uma maneira de fazer isso sem ter que recorrer a isso. Ou definitivamente saber que isso não pode ser feito também seria uma resposta válida.)

(Ou devo mudar para o common-lisp ?;))

Para minhas necessidades, eu ficaria feliz com sh ou bash , com certeza, e possivelmente zsh , embora se houver uma maneira de fazer isso de uma forma portável máxima (ou seja, sh ), que seria uma resposta melhor, IMHO.

    
por lindes 27.02.2015 / 04:06

2 respostas

2

Isso realmente não é sobre case per se: você precisa de um mecanismo para verificar se uma palavra está presente em uma lista. Com o bash você pode fazer isso:

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

Mais portably, basta virar o caso

srsp="this|that|or|the|other"
something=other
case "|$srsp|" in
    *"|$something|"*) echo in;;
    *) echo not;;
esac
    
por 27.02.2015 / 04:28
1

Você pode usar eval , é claro, mas é bastante difícil de manejar. Talvez encapsulá-lo em uma função para que você possa evitar muitas barras invertidas fora do complexo 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

Se você sabe que pode restringir o conjunto de caracteres na lista, talvez execute uma filtragem case antes do eval para segurança, algo como

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
}
    
por 02.03.2015 / 14:35

Tags