Вот решение, использующее просто bash
, без регулярных выражений:
> cat filter.sh
#!/bin/bash
declare -A lower=()
declare -A upper=()
while IFS= read -r line; do
eval "words=( $(tr ',' ' ' <<< "$line") )"
for w in "${words[@]}"; do
[[ "${w^^}" = "$w" ]] && upper["$w"]=1 || lower["$w"]=1
done
done
for u in "${!upper[@]}"; do
exists=${lower["${u,,}"]+foo}
[[ -n "$exists" ]] && echo "$u"
done
Здесь я использую пару приемов.
Во-первых, я использую ассоциативные массивы, чтобы отсеять повторы.Например, если "HELLO123"
появляется в файле несколько раз, он будет засчитан только один раз.
Во-вторых, я анализирую CSV, используя tr
для замены запятых на пробелы, а затем eval
для анализа строки в массиве, используя тот факт, что отдельные словавсегда заключено в двойные кавычки.
Наконец, я использую [[ "${w^^}" = "$w" ]]
в качестве теста, чтобы проверить, содержит ли слово все заглавные буквы.Синтаксис ${w^^}
представляет собой bash-ism, который преобразует переменную в верхний регистр.Я также использую ${u,,}
во втором цикле, который преобразует $u
в строчные буквы.Обратите внимание, что если у вас есть слово с сочетанием прописных и строчных букв, оно будет засчитано как строчное.Если это не соответствует вашим ожиданиям, вы можете изменить логику.
Первый цикл просто читает с stdin
, разбивает каждую строку на отдельные слова и затем классифицирует каждое слово как в верхнем или нижнем регистре.Опять же, я использую ассоциативные массивы, так что каждое слово (независимо от регистра) учитывается только один раз.
Второй цикл просто проходит по ключам ассоциативного массива upper
({${!upper[@]}
), которые являются только всеми заглавными словами, встречающимися на входе.Для каждого слова проверяется, встречалось ли также совпадение строчных слов.Синтаксис ${lower["${u,,}"]+foo}
просто проверяет, существует ли строчное слово в массиве lower
.Часть foo
- это просто произвольная строка.Вы также можете использовать bar
или exists
или abc
.Вот как вы проверяете наличие ключа в ассоциативном массиве в bash
.Если ключ существует в массиве, выражение будет иметь значение "foo"
, в противном случае оно будет выглядеть как пустая строка.Вот что проверяет последующий -n
тест.
Пример:
> cat input.txt
"hello","2018-11-19","unitelife"
"world","2018-11-09","unitelife"
"foo","2018-11-16","unitelife"
"bar","2018-10-05","unitelife"
"hello123","2018-09-06","unitelife"
"HELLO123","2018-11-18","unitelife"
"FOO","2018-11-20","unitelife"
"WOWMUCHHAPPY","2018-10-20","unitelife"
"suchjoy","2017-11-28","unitelife"
> cat input.txt | ./filter.sh
FOO
HELLO123
ПРИМЕЧАНИЕ: Пожалуйста, не используйте eval
в рабочем коде.Он подвержен всевозможным злоупотреблениям и неудачам из-за неожиданных вещей, появляющихся на входе.Например, рассмотрим, что произойдет, если вы вставите в строку ввода следующую строку:
"); rm -rf *; foo=("
Тогда eval
в конечном итоге оценит строку "words=(); rm -rf *; foo=()"
.Определенно не хорошо.Я использовал здесь только eval как быстрый и грязный способ разбора CSV.Есть намного лучших (и более безопасных ) способов анализа CSV в bash
.Смысл этого решения заключается в использовании ассоциативных массивов для отслеживания прописных и строчных слов при фильтрации дубликатов.
Edit: Также обратите внимание, что появляются FOO
и HELLO123
вышли из строя на выходе.Это связано с тем, что ассоциативные массивы не хранят ключи в том порядке, в котором вы их создали.Поэтому, когда вы делаете ${!hash[@]}
, то это порядок, в котором будут располагаться ключи. Если это проблема для вас, вы можете сохранить отдельный регулярный массив для сохранения порядка.