Probar si existe una cadena dentro de una matriz de cadenas

Probar si existe una cadena dentro de una matriz de cadenas

Estoy escribiendo un script que compara las primeras 3 letras de cada línea (usando cortar para obtenerlas) en un archivo con cadenas dentro de una matriz. Ya busqué pero las soluciones que encontré no funcionaron en mi sistema.

Ahora mismo se ve así:

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}

La línea se corta correctamente, pero algo durante la prueba arroja un falso positivo, ya que no importa cuáles sean las primeras 3 letras, devuelve una "Coincidencia".

Cuando revisé el script con -x me mostró, en lugar de la prueba real, que usaba

[[ -n 1 ]]

Y cuando lo probé con la [ ]expresión mostró un1

¿Comprueba cada carácter de la matriz y no solo las palabras completas, o hay algún otro problema?

Si no hay problema, ¿hay otra manera de comparar las primeras 3 letras de una línea con todo lo que hay dentro de una matriz antes de continuar con la siguiente?

Como nota al margen: de hecho estoy ejecutando Bash 4, por lo que las matrices asociativas deberían funcionar

Respuesta1

El error básico es que en realidad no estás declarando una matriz asociativa:

$ 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]}

$

No estoy del todo seguro de cómo lo maneja bash y por qué solo necesita un 1, pero estoy seguro de que no es una matriz asociativa. Como se explica en man bash(el énfasis es mío):

Una matriz indexada se crea automáticamente si se asigna alguna variable utilizando la sintaxis nombre[subíndice]=valor. El subíndice se trata como una expresión aritmética que debe evaluarse como un número. Para declarar explícitamente una matriz indexada, use declarar -a nombre (consulte COMANDOS INTEGRADOS DE SHELL a continuación). declarar -también se acepta un nombre[subíndice]; el subíndice se ignora.

Las matrices asociativas se crean usando declarar -A nombre.

Entonces, prueba esto y funcionará como esperas:

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

Dicho esto, tu guión es un poco más complejo de lo que necesitas. Aquí hay una versión más simple que utiliza el mismo enfoque:

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

Aunque probablemente lo haría así:

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

Luego, ejecute el script con el nombre del archivo de destino como argumento:

script.sh /Foo/Bar.log"

Respuesta2

Usaría una invocación de una herramienta de procesamiento de texto para procesar texto., no varias herramientas para cada línea de entrada:

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

Usaría un bucle si necesitara ejecutar una aplicación particular para cada línea de entrada, pero si es solo procesamiento de texto, como enviar la línea a algún archivo, entonces awkpuede hacerlo de la siguiente manera:

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

información relacionada