Testando se existe uma String dentro de um Array de Strings

Testando se existe uma String dentro de um Array de Strings

Estou escrevendo um script que compara as três primeiras letras de cada linha (usando cut para obtê-las) em um arquivo com strings dentro de um array. Já procurei, mas as soluções que encontrei não funcionaram no meu sistema.

No momento está assim:

weekdays=([Mon]=1 [Tue]=1 [Wed]=1 [Thu]=1 [Fri]=1 [Sat]=1 [Sun]=1)
input="/Foo/Bar.log"

while read -r line
do

cutline="$(echo ${line} | cut -c 1-3"

if [[ ${weekdays["$cutline"]} ]]
then
echo "Match"
else
echo "No Match"
fi

done < ${input}

A linha é cortada corretamente, mas algo durante o teste retorna um falso positivo, pois não importa quais sejam as 3 primeiras letras, ela retorna uma "correspondência".

Quando verifiquei o script com -x ele me mostrou, em vez do teste real que ele usou

[[ -n 1 ]]

E quando testei com a [ ]expressão ele mostrou um1

Ele verifica todos os caracteres da matriz e não apenas as palavras inteiras, ou há algo mais errado com isso?

Se não houver problema, existe outra maneira de comparar as 3 primeiras letras de uma linha com tudo dentro de uma matriz antes de continuar com a próxima?

Como observação lateral: estou realmente executando o Bash 4, então matrizes associativas devem funcionar

Responder1

O erro básico é que você não está realmente declarando um array associativo:

$ weekdays=(["Mon"]=1 ["Tue"]=1 ["Wed"]=1 ["Thu"]=1 ["Fri"]=1 ["Sat"]=1 ["Sun"]=1)
$ echo ${weekdays[@]}
1
$ echo ${weekdays[0]}
1
$ echo ${weekdays[2]}

$

Não tenho muita certeza de como o bash está lidando com isso e por que é necessário apenas um 1, mas tenho certeza de que não é um array associativo. Conforme explicado em man bash(ênfase minha):

Uma matriz indexada é criada automaticamente se qualquer variável for atribuída usando a sintaxe nome[subscrito]=valor. O subscrito é tratado como uma expressão aritmética que deve ser avaliada como um número. Para declarar explicitamente um array indexado, use declare -a name (veja SHELL BUILTIN COMMANDS abaixo). declare -a name[subscript] também é aceito; o subscrito é ignorado.

Matrizes associativas são criadas usando declare -A name.

Então, tente isso e funcionará conforme o esperado:

declare -A weekdays=(["Mon"]=1 ["Tue"]=1 ["Wed"]=1 ["Thu"]=1 ["Fri"]=1 ["Sat"]=1 ["Sun"]=1)

Dito isto, seu script é um pouco mais complexo do que você precisa. Aqui está uma versão mais simples usando a mesma abordagem:

#!/bin/bash
declare -A weekdays=(["Mon"]=1 ["Tue"]=1 ["Wed"]=1 ["Thu"]=1 ["Fri"]=1 ["Sat"]=1 ["Sun"]=1)
input="/Foo/Bar.log"

cut -c 1-3 "$input" | while read -r line; do
    if [[ ${weekdays["$line"]} ]]
    then
            echo "Match : $cutline : ${weekdays[$line]}"
    else
            echo "No Match"
    fi
done    

Embora eu provavelmente faria assim:

#!/bin/bash
cut -c 1-3 "$1" | while read -r line; do
        case $line in
        "Mon"|"Tue"|"Wed"|"Thu"|"Fri"|"Sat"|"Sun")
                        echo yes;;
                *)
                        echo no;;
        esac
done

Em seguida, execute o script com o nome do arquivo de destino como argumento:

script.sh /Foo/Bar.log"

Responder2

Eu usaria uma invocação de uma ferramenta de processamento de texto para processar texto, não várias ferramentas para cada linha de entrada:

awk -v 'weekday=(Mon|Tue|Wed|Thu|Fri|Sat|Sun)' '
  {print ($0 ~ "^" weekday ? "" : "No ") "Match"}' < "$input"

Você usaria um loop se precisasse executar um aplicativo específico para cada linha da entrada, mas se for apenas processamento de texto, como enviar a linha para algum arquivo, você awkpode fazer isso como:

awk -v 'weekday=Mon|Tue|Wed|Thu|Fri|Sat|Sun' '
  (day = substr($0, 1, 3)) ~ weekday {
    print substr($0, 4) > day ".txt"
  } < "$input"

informação relacionada