O caractere Colon está bagunçando minha correspondência de padrão no Bash

1

Bom dia. Eu estou atualmente na hora 10 deste estúpido Catch-22 dentro do BASH

Eu tenho uma variável $line , que pode conter qualquer uma das seguintes strings:

  • line="READ CACHE IS: ENABLED"
  • line="BLOCKS READ CACHE AND SENT TO INITIATOR = 2489338280"
  • line="ECC REREADS/ ERRORS ALGORITHM PROCESSED UNCORRECTED"
  • line="READ: 2513550726 22 0 2513550748 2513550748 27768.965 0"
  • line="1 RAW_READ_ERROR_RATE PO-R-- 100 100 016 - 0"
  • line="0x22 GPL R/O 1 READ STREAM ERROR LOG"
  • line="READ: DISABLED"

Eu tenho um script que compara a variável $line com alguns padrões:

if [[ ${line} == *"RAW_READ_ERROR_RATE"* ]] || 
[[ ${line} == "READ\:"* ]] && 
[[ ${line} != *"READ: DISABLED"* ]]; then

devReadErr=$(echo "$line" | awk '{print $8}')

Aqui está o problema. O cólon está estragando tudo. Eu tentei todas as formas possíveis de formatar o padrão para satisfazer as duas possibilidades de line="1 RAW_READ_ERROR_RATE PO-R-- 100 100 016 - 0" ou line="READ: 2513550726 22 0 2513550748 2513550748 27768.965 0" Quando eu escapar do: como mostrado acima. Eu posso satisfazer line="1 RAW_READ_ERROR_RATE PO-R-- 100 100 016 - 0" , mas não line="READ: 2513550726 22 0 2513550748 2513550748 27768.965 0" . Se eu tirar a fuga, satisfarei line="READ: 2513550726 22 0 2513550748 2513550748 27768.965 0" não line="1 RAW_READ_ERROR_RATE PO-R-- 100 100 016 - 0"

Execução da amostra 1:

line="1 RAW_READ_ERROR_RATE PO-R-- 100 100 016 - 0"

        if [[ ${line} == *"RAW_READ_ERROR_RATE"* ]] || 
           [[ ${line} == "READ\:"* ]] && 
           [[ ${line} != *"READ: DISABLED"* ]]; then

          devReadErr=$(echo "$line" | awk '{print $8}')
        fi

echo $devReadErr

Saída da execução 1:

0

Execução de amostra 2:

line="READ: 2513550726 22 0 2513550748 2513550748 27768.965 0"

        if [[ ${line} == *"RAW_READ_ERROR_RATE"* ]] || 
           [[ ${line} == "READ\:"* ]] && 
           [[ ${line} != *"READ: DISABLED"* ]]; then

          devReadErr=$(echo "$line" | awk '{print $8}')
        fi

echo $devReadErr

Saída da execução 2:

<null>

Execução de amostra 3:

line="READ: 2513550726 22 0 2513550748 2513550748 27768.965 0"

        if [[ ${line} == *"RAW_READ_ERROR_RATE"* ]] || 
           [[ ${line} == "READ:"* ]] && 
           [[ ${line} != *"READ: DISABLED"* ]]; then

          devReadErr=$(echo "$line" | awk '{print $8}')
        fi

echo $devReadErr

Saída da execução 3:

0

Como obtenho o melhor dos dois mundos?

    
por AfroJoe 19.09.2018 / 04:18

2 respostas

2

Você deve remover o \ na frente de : nesse segundo teste ou tentará corresponder a um caractere% \ literal.

Estas não são correspondências de expressões regulares que você está fazendo, mas o padrão de globalização de shell corresponde (assim como na linha de comando quando você está usando * em padrões). Isso realmente não importa neste caso.

Suponho que você queira extrair o 20 das duas primeiras strings e armazená-lo em devReadErr , mas não quando a linha ler READ: DISABLED . Isso é exatamente o que seu código faz se o \ for removido:

if  [[ ${line} == *"RAW_READ_ERROR_RATE"* ]] ||
    [[ ${line} == "READ:"* ]] &&
    [[ ${line} != *"READ: DISABLED"* ]]; then

    devReadErr=$(echo "$line" | awk '{print $2}')

fi

Outra maneira de fazer a mesma coisa:

if [[ "$line" != *'DISABLED' ]]; then
    devReadErr=${line##* }
fi

Isso extrai o número como a string após o último caractere de espaço em $line se a string não terminar com a palavra DISABLED . Isso evita os echo e awk .

Se isso fizer parte de um loop maior que analisa um arquivo linha por linha, sugiro escrevê-lo em awk ou alguma outra linguagem criada para analisar texto. Veja, por exemplo, Por que usar um loop de shell para processar texto é considerado uma prática ruim? .

    
por 19.09.2018 / 07:51
0

Eu suspeito que você queira:

if [[ $line = *RAW_READ_ERROR_RATE* || 
      $line = READ:* && $line != *"READ: DISABLED"* ]]; then

O operador && [[...]] tem precedência sobre || , mas o operador && shell tem a mesma precedência que || .

Ou para explicitar:

if [[ $line = *RAW_READ_ERROR_RATE* || 
      ($line = READ:* && $line != *"READ: DISABLED"*) ]]; then

Ou usando os operadores de && / || e vários [[...]] s:

if [[ $line = *RAW_READ_ERROR_RATE* ]] || { 
      [[ $line = READ:* ]] && [[ $line != *"READ: DISABLED"* ]]; }; then

Ou altere a ordem:

if [[ $line = READ:* ]] && [[ $line != *"READ: DISABLED"* ]] ||
   [[ $line = *RAW_READ_ERROR_RATE* ]]; then

Ou use um padrão que corresponda a todos:

if [[ $line = @(*RAW_READ_ERROR_RATE*|!(!(*READ:*)|*READ:\ DISABLED*)) ]]; then

Sem parênteses / chaves, o seu é lido como:

if [[ ($line = *RAW_READ_ERROR_RATE* || 
     $line = READ:*) && $line != *"READ: DISABLED"* ]]; then

Isso não deve impedir que as linhas correspondentes contenham RAW_READ_ERROR_RATE , desde que não contenham READ: DISABLED .

    
por 19.09.2018 / 15:15