
Я пишу скрипт, который сравнивает первые 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"