Я понял это, когда собирался опубликовать вопрос.
Команда «чтение» в моем первом цикле покажет строку только после того, как в консоли появится символ новой строки. Это означает, что мой сценарий не мог ответить на приглашение, пока не появилась новая строка; это означало, что окно было закрыто.
Чтобы исправить это, я изменил проверку, чтобы искать строку, которая появляется над фактической подсказкой, за которой следует сон на 1/100 секунды перед выдачей моей записи.
Это решение позволяет автоматически останавливать процессы загрузки.
С grep:
cat poem.txt \
| grep -Evi -e '\<an\>.*\<the\>' -e '\<the\>.*\<an\>' \
| grep -Eci -e '\<(an|the)\>'
При этом подсчитываются совпавших строк . Вы можете найти альтернативный синтаксис, который подсчитывает общее количество совпадений ниже.
Разбивка:
Команда frist grep отфильтровывает все строки, содержащие как 'an', так и 'the'. Вторая команда grep подсчитывает те строки, которые содержат либо «an», либо «the».
Если вы удалите c
из второго grep -Eci
, вы увидите, что все совпадения выделены.
Детали:
Опция -E
включает расширенный синтаксис выражений (ERE )для grep.
Параметр -i
сообщает grep, что он должен соответствовать регистру -без учета регистра
Опция -v
указывает grep инвертировать результат (, т. е. строки соответствия , не содержащие шаблон)
Опция -c
указывает grep выводить количество совпавших строк вместо самих строк
Узоры:
\<
соответствует началу слова (спасибо @glenn -jackman)\>
соответствует концу слова (спасибо @glenn -jackman)--> Таким образом, мы можем быть уверены, что слова , содержащие 'the' или 'an' (, такие как 'pan' )
, не совпадают.grep -Evi -e '\<an\>.*\<the\>'
соответствует всем строкам , кроме , содержащим 'an... the'
Аналогично, grep -Evi -e '\<the\>.*\<an\>'
соответствует всем строкам , не , содержащим 'the... an'
grep -Evi -e '\<an\>.*\<the\>' -e '\<the.*an\>'
представляет собой комбинацию 3. и 4.
grep -Eci -e '\<(an|the)\>'
соответствует всем строкам, содержащим «an» или «the» (, окруженным пробелом или началом/концом строки ), и печатает количество совпадающих строк
РЕДАКТИРОВАТЬ 1:Используйте \<
и \>
вместо ( |^)
и ( |$)
, как предложил @glenn -jackman
РЕДАКТИРОВАТЬ 2:Чтобы подсчитать количество совпадений вместо количества совпавших строк, используйте следующее выражение:
cat poem.txt \
| grep -Evi -e '\<an\>.*\<the\>' -e '\<the\>.*\<an\>' \
| grep -Eio -e '\<(an|the)\>' \
| wc -l
Здесь используется опция -o
команды grep, которая печатает каждое совпадение в отдельной строке (и больше ничего ), а затем wc -l
для подсчета строк.
Следующая программа GNU awk
должна помочь:
awk '(/(^|\W)[Tt]he(\W|$)/ && !/(^|\W)[Aa]n(\W|$)/) || (/(^|\W)[Aa]n(\W|$)/ && !/(^|\W)[Tt]he(\W|$)/) {c++} END{print c}' poem.txt
Это увеличит значение счетчика c
, если
(^|\W)[Tt]he(\W|$)
(первой -букве -регистру -нечувствительна the
, предшествует не -составляющая слова(\W
)или начало строки (^
), а затем не -составляющая слова(\W
)или конец -строки ($
)), но не(^|\W)[Aa]n(\W|$)
(изолированная первая -буква -регистр -нечувствительныйan
)-ИЛИ-(^|\W)[Aa]n(\W|$)
, но не(^|\W)[Tt]he(\W|$)
В конце выведите значение c
.
Его можно сформулировать немного короче, используя \<
и \>
для «начала -слова -» и «конца -слова -»:
awk '(/\<[Tt]he\>/ && !/\<[Aa]n\>/) || (/\<[Aa]n\>/ && !/\<[Tt]he\>/) {c++} END{print c}' poem.txt
Еще короче было бы:
awk '/\<[Tt]he\>/ != /\<[Aa]n\>/ {c++} END{print c}' poem.txt
, так как неравенство всегда истинно только в том случае, если одно из них, но не оба (и ни одно из )из an
и the
присутствуют на линии.
Для этого подхода требуется GNU awk
, поскольку конструкции \W
и \<
/\>
являются расширениями GNU для расширенного синтаксиса регулярных выражений (, но \<
/\>
также понимаются . ] Регулярные выражения BSD).
Обратите внимание, что конструкция конвейера, показанная вами в вашей собственной попытке решения, не будет работать, так как вызов grep
с файлом в качестве входного параметра заменяет чтение со стандартного ввода, поэтому первая часть конвейера просто исчезнет незамеченной, а вывод полностью связан с последней частью (, которая ищет вхождения an
, даже встроенные в другие слова ).
perl -nE 'END {say $c+0} ++$c if /\bthe\b/i xor /\ban\b/i' file
gawk 'END {print c+0} /\<the\>/ != /\<an\>/ {++c}' IGNORECASE=1 file
Сравнение результатов сопоставления каждого выражения может дать желаемый результат.
Например, результатом сопоставления \<the\>
может быть либо 0, либо 1. Если результат другого совпадения такой же, то оба регулярных выражения либо найдены, либо не найдены, и строка не должна учитываться. Если они различаются, это означает, что одно совпадение было найдено, а другое нет, поэтому счетчик увеличивается.
gawk имеет встроенную -в xor()
функцию:
gawk 'END {print c+0} xor(/\<the\>/,/\<an\>/) {++c}' IGNORECASE=1 file
Вы можете сделать это с помощью GNU grep и PCRE с нулевыми -утверждениями длины:
grep -iP '(?=.*\bthe\b)(?!.*\ban\b)|(?=.*\ban\b)(?!.*\bthe\b)' poem.txt
Where is the misty shark?
...
Eat an orange.
...
grep -ciP '(?=.*\bthe\b)(?!.*\ban\b)|(?=.*\ban\b)(?!.*\bthe\b)' poem.txt
9
Та же функция доступна в perl (, где она возникла ), и perl может присутствовать на машинах, где нет GNU grep.
Эта команда GNU awk сделает то, что вам нужно:
$ awk '
function lnxor(a,b) {return !(a&&b||(!a&&!b))};
{ str=tolower($0); a=(str~/\<the\>/); b=(str~/\<an\>/);
c=lnxor(a,b); count+=c
} END {print count}
' poem.txt
9
Инксор — это л логическое n от исключающее ИЛИ из a
и b
.
Где a
истинно, если строка содержит the
и b истинно, если строка содержит an
.
Таким образом, довольно легко распространить идею на большее количество слов.
В GNU awk также можно использовать IGNORECASE
(, установив его в не -ноль )вместо tolower()
, чтобы игнорировать случай совпадения строки с регулярным выражением. Однако это изменит все операции со строками и регулярными выражениями, включая эффекты FS и RS. Поскольку изменение (, которое сработает в данном случае, ), его следует использовать с большей осторожностью. Это не относится к какому-то конкретному тесту.
Метод, использующий perl
, в котором мы суммируем логические значения регулярных выражений и, используя это в качестве индекса, выбираем из анонимного массива соответствующий приращение в зависимости от того, не найдено ни одного, найдено любое или и то, и другое.
perl -lne '$k += (0,1,0)[/\ban\b/i+/\bthe\b/i]}{print 0+$k' poems.txt
9
Использование gawk
иwc
:
gawk 'xor(/\<an\>/,/\<the\>/)' IGNORECASE=1./poem.txt | wc -l
Это позволит вам выводить строки, содержащие любое количество любого набора слов, и будет работать с использованием любого awk в любой оболочке на каждом компьютере Unix:
$ cat tst.awk
BEGIN {
FS = "[^[:alnum:]_]+"
split(tolower(tgts),tmp)
for (i in tmp) {
targets[tmp[i]]
}
tgtsRequired = (tgtsRequired ? tgtsRequired : 1)
}
{
delete present
for (i=1; i<=NF; i++) {
present[tolower($i)]
}
tgtsPresent = 0
for (target in targets) {
tgtsPresent += (target in present ? 1 : 0)
}
}
tgtsPresent == tgtsRequired
$ awk -v tgts='the an' -f tst.awk poem.txt
Where is the misty shark?
The small reef roughly fights the mast.
Where is the small gull?
The gull grows like a clear pirate.
Eat an orange.
Elvis Aaron Presley also known simply as the Elvis
He is also referred to as the King
The best-selling solo music artist of all time
He was the most commercially successful artist in many genres
$ awk -v tgts='the an' -v tgtsRequired=2 -f tst.awk poem.txt
Elvis in the 1970s has numerous jumpsuits including an eagle one.
$ awk -v tgts='the an eagle' -v tgtsRequired=2 -f tst.awk poem.txt
$ awk -v tgts='the an eagle' -v tgtsRequired=3 -f tst.awk poem.txt
Elvis in the 1970s has numerous jumpsuits including an eagle one.
Под "словом" подразумевается любая непрерывная строка буквенных -цифровых -или -символов подчеркивания.