Escreva uma função que verifique se uma string começa ou contém algo

Escreva uma função que verifique se uma string começa ou contém algo

Quero escrever uma função que verifique se uma determinada variável, digamos, varcomeça com qualquer uma das palavras em uma determinada lista de strings. Esta lista não mudará.

Para instanciar, vamos fingir que quero verificar se varcomeça aacom abcou 3@3.

Além disso, quero verificar se varcontém o caractere >.

Digamos que esta função seja chamada check_func. Meu uso pretendido é algo como

if check_func "$var"; then
    do stuff
fi

Por exemplo, deveria "fazer coisas" para aardvark, abcdef, [email protected]e 12>5.


eu tenho vistoesta pergunta SOonde um usuário fornece parte do trabalho:

beginswith() { case $2 in "$1"*) true;; *) false;; esac; }

Minha ideia é iterar a lista mencionada acima e usar essa função. Minha dificuldade reside em não entender exatamente como a saída (ou o que quer que substitua o retorno) deve ser feita para que isso funcione.

Responder1

check_prefixes () {
    value=$1

    for prefix in aa abc 3@3; do
        case $value in
            "$prefix"*) return 0
        esac
    done

    return 1
}

check_contains_gt () {
    value=$1

    case $value in
        *">"*) return 0
    esac

    return 1
}

var='aa>'
if check_prefixes "$var" && check_contains_gt "$var"; then
    printf '"%s" contains ">" and starts with one of the prefixes\n' "$var"
fi

Dividi os testes em duas funções. Ambos usam case ... esace retornam sucesso (zero) assim que isso puder ser determinado. Se nada corresponder, a falha (1) será retornada.

Para tornar a lista de prefixos mais dinâmica, pode-se escrever a primeira função como

check_prefixes () {
    value=$1
    shift

    for prefix do
        case $value in
            "$prefix"*) return 0
        esac
    done

    return 1
}

(o valor a ser inspecionado é o primeiro argumento, que salvamos valuee depois shiftretiramos da lista de argumentos da função; então iteramos sobre os argumentos restantes) e então o chamamos como

check_prefixes "$var" aa abc 3@3

A segunda função poderia ser alterada de maneira semelhante, para

check_contains () {
    value=$1
    shift

    case $value in
        *"$1"*) return 0
    esac

    return 1
}

(para verificar alguma substring arbitrária), ou

check_contains_oneof () {
    value=$1
    shift

    for substring do
        case $value in
            *"$substring"*) return 0
        esac
    done

    return 1
}

(para verificar qualquer uma de várias substrings)

Responder2

Para festa:

Usando as propriedades do regex você pode escrever startcom ^e containpor nada.

A lista de regexes a serem verificadascomeçarcom aa abcou 3@3econtém >é:

^aa ^abc ^3@3 >

Faça disso umapropriadamentelista citada e peça ao bash para usar regexes ( =~):

check_func() {
               matched=1
               for test_regex in '^aa' '^abc' '^3@3' '>'; do
                   if [[ $var =~ $test_regex ]] ; then
                       matched=0
                       break 
                   fi
               done
               return "$matched"
              }

var='aaIsAMatch'
if check_func; then
    echo "A match was found"
fi

A função codificou a lista de correspondências e o nome da var.

Fornecendo a lista de regex em uma variável de array e o valor a ser testado no primeiro argumento:

check_func() {
               local matched; matched=1
               for t in "${test_regex[@]}"; do
                   [[ $1 =~ $t ]] && { matched=0; break; } 
               done
               return "$matched"
              }


test_regex=('^aa' '^abc' '^3@3' '>')

if check_func 'aaIsAMatch'; then
    echo "A match was found"
fi

A função poderia ser melhorada para usar o nome de uma variável (em vez de um valor) como primeiro argumento.

posix

Como não há regex em shells posix e a única maneira de testar é uma instrução case, devemos usar uma instrução case. Infelizmente, para shells mais antigos ([nenhum globo estendido disponível][1]) devemos fazer um loop para fazer todos os testes. E os globos precisam ser:

'aa*' 'abc*' '3@3*' '*>*'

Um exemplo de script que testa diversas strings de entrada em vários globs:

check_func() { :
           matched=1
       value=$1; shift
           for t in "$@"; do
               case $value in $t) matched=0; #break;; esac
                  echo "matched $value with $t"
                  ;;
       esac
       done
           return "$matched"
         }


for var in abdg wabcde aadef abcde 3@3hello hmm3@3hell 'we>we' 'a>dfff' 'dfd>' 'a> de' 'a*> fg'; do
if check_func "$var" 'aa*' 'abc*' '3@3*' '*>*'; then
        echo "========A match was found for \"$var\""
fi
done

Uma versão mais simples da função para corresponder exatamente à sua solicitação:

check_func() { :
               matched=1
               value=$1; shift
                   for t in "$@"; do
                       case $value in $t) matched=0; break;; esac
                   done
               return "$matched"
             }

Responder3

Veja o que a caseinstrução faz: leve o segundo parâmetro para a função ( $2). Se corresponder ao padrão "$1"*, ou seja, o primeiro argumento da função seguido por qualquer coisa, execute truee termine a caseinstrução. truenão faz nada e retorna o status 0. Caso contrário, se corresponder *, ou seja, qualquer coisa, execute falsee finalize a caseinstrução. falsenão faz nada e retorna o status 1. Assim, a caseinstrução terá o status 0 se o segundo parâmetro começar com o primeiro parâmetro e 1 caso contrário. Como esta é a última (e única) instrução da função, a função retornará 0 se o segundo parâmetro começar com o primeiro parâmetro e 1 caso contrário.

Declarações condicionais, como ifno shell, consideram uma afirmação verdadeira se retornar 0 e falsa caso contrário. Portanto, if beginswith "$var" "string"; then echo yes; else echo no; fiimprime yesse o valor de varcomeça com stringe nocaso contrário.

Existem várias maneiras alternativas de escrever esta função. Por exemplo, o autor poderia ter usado return 0ou return 1em vez de truee false, já que são a última instrução da função. A forma como a função foi escrita torna possível usar seu corpo diretamente, sem envolvê-la em uma função, apenas alterando as referências aos parâmetros da função ( $1e $2) para quaisquer strings com as quais você deseja trabalhar.

Para permitir vários prefixos, itere sobre eles em um loop. Assim que você encontrar um prefixo correspondente, retorne da função, com um status verdadeiro (0). Se nenhum dos prefixos corresponder, retorne um status falso (convencionalmente 1).

# begins_with STRING PREFIX1 PREFIX2...
# Test if STRING starts with any of PREFIX1, PREFIX2, ...
begins_with () {
  string=$1
  shift
  for prefix in "$@"; do
    case "$string" in
      "$prefix"*) return 0;;
    esac
  done
  return 1
}

if begins_with "$var" 'aa' 'abc' '3@3'; then
  echo "The value starts with one of the permitted prefixes"
fi

Para testar um sufixo, use o padrão *"$suffix"em vez de "$prefix"*. Para testar uma substring, use *"$substring"*. Observe que as aspas duplas são necessárias aqui, caso contrário a variável seria interpretada como um padrão. Por exemplo:

suffix='?'
case "$var" in
  *"$suffix") echo "The value of var ends with a question mark";; 
esac
case "$var" in
  *$suffix) echo "The value of var is not empty";; 
esac

Responder4

Revisado com base no esclarecimento da pergunta: Isso é menos elegante (e muito menos flexível), mas mais compacto que as outras respostas,

check_func() {
        case "$1" in
            ( aa* | abc* | 3@3*  | *">"*)
                return 0
        esac
        return 1
}

Isso retorna verdadeiro para aardvark, abcdefe . E, claro, também , e .[email protected]12>5aard>varkabc<def>ghi3@3>3

informação relacionada