Проверка существования строки внутри массива строк

Проверка существования строки внутри массива строк

Я пишу скрипт, который сравнивает первые 3 буквы каждой строки (используя cut, чтобы получить их) в файле со строками внутри массива. Я уже поискал, но найденные мной решения не работали в моей системе.

Сейчас это выглядит так:

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}

Линия разрезается правильно, но что-то во время теста возвращает ложный положительный результат, поскольку независимо от первых трех букв возвращается «Совпадение».

Когда я проверил скрипт с помощью -x, он показал мне, что вместо реального теста он использовал

[[ -n 1 ]]

И когда я проверил это с помощью [ ]выражения, оно показало1

Проверяет ли он каждый отдельный символ в массиве, а не только целые слова, или с ним что-то еще не так?

Если проблем нет, есть ли другой способ сравнить первые 3 буквы строки со всем массивом, прежде чем перейти к следующей?

Примечание: я действительно использую Bash 4, так что ассоциативные массивы должны работать.

решение1

Основная ошибка заключается в том, что вы на самом деле не объявляете ассоциативный массив:

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

$

Я не совсем уверен, как bash с этим справляется и почему он просто принимает 1, но я уверен, что это не ассоциативный массив. Как объяснено в man bash(выделено мной):

Индексированный массив создается автоматически, если любая переменная назначается с использованием синтаксиса name[subscript]=value. Нижний индекс рассматривается как арифметическое выражение, которое должно быть вычислено как число. Чтобы явно объявить индексированный массив, используйте declare -a name (см. ВСТРОЕННЫЕ КОМАНДЫ ОБОЛОЧКИ ниже). Также допускается declare -a name[subscript]; нижний индекс игнорируется.

Ассоциативные массивы создаются с помощью команды declare -A name.

Итак, попробуйте сделать так, и все будет работать так, как вы ожидаете:

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

Тем не менее, ваш скрипт немного сложнее, чем вам нужно. Вот более простая версия, использующая тот же подход:

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

Хотя я бы, наверное, сделал так:

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

Затем запустите скрипт, указав в качестве аргумента имя целевого файла:

script.sh /Foo/Bar.log"

решение2

Я бы использовал один вызов инструмента для обработки текста, чтобы обработать текст, а не несколько инструментов для каждой строки ввода:

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

Вы бы использовали цикл, если бы вам нужно было запустить определенное приложение для каждой строки ввода, но если это просто обработка текста, например вывод строки в какой-то файл, то это awkможно сделать так:

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

Связанный контент