
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ê awk
pode 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"