Подсчет палиндромов в текстовом файле

После этого streamа BASH Поиск палиндромов в TXT-файле, я не могу понять, что я делаю неправильно с моим скриптом.

#!/bin/bash search() { tr -d '[[:punct:][:digit:]@]' \ | sed -E -e '/^(.)\1+$/d' \ | tr -s '[[:space:]]' \ | tr '[[:space:]]' '\n' } search "$1" paste <(search <"$1") <(search =3) { print $1 }' \ | sort | uniq -c 

Все im, получаемые из этого скрипта, выводятся из всего текстового файла. Я хочу только выводить палиндромы> = 3 и считать их такими, как

425

120 не

и т. д. Мой текстовый файл называется sample.txt, и каждый раз я запускаю скрипт с помощью: cat sample.txt | source palindrome Я получаю сообщение ‘bash:: Нет такого файла или каталога’.

Использование awk и sed

 awk 'function palindrome(str) {len=length(str); for(k=1; k<=len/2+len%2; k++) { if(substr(str,k,1)!=substr(str,len+1-k,1)) return 0 } return 1 } {for(i=1; i<=NF; i++) {if(length($i)>=3){ gsub(/[^a-zA-Z]/,"",$i); if(length($i)>=3) {$i=tolower($i); if(palindrome($i)) arr[$i]++ }} } } END{for(i in arr) print arr[i],i}' file | sed -E '/^[0-9]+ (.)\1+$/d' 

Протестировано на 1.2GB- файле и время выполнения ~ 4m 40s (i5-6440HQ @ 2.60GHz/4 cores/16GB)

Объяснение:

 awk ' function palindrome(str) # Function to check Palindrome { len=length(str); for(k=1; k<=len/2+len%2; k++) { if(substr(str,k,1)!=substr(str,len+1-k,1)) return 0 } return 1 } { for(i=1; i<=NF; i++) # For Each field in a record { if(length($i)>=3) # if length>=3 { gsub(/[^a-zA-Z]/,"",$i); # remove non-alpha character from it if(length($i)>=3) # Check length again after removal { $i=tolower($i); # Covert to lowercase if(palindrome($i)) # Check if it's palindrome arr[$i]++ # and store it in array } } } } END{for(i in arr) print arr[i],i}' file | sed -E '/^[0-9]+ (.)\1+$/d' 

sed -E '/^[0-9]+ (.)\1+$/d' : Из окончательного результата проверьте, какие строки состоят из только повторяющихся хрусталей, таких как AAA , BBB т. д. и удаляют их.


Старый ответ (до EDIT)

Вы можете выполнить следующие шаги, если хотите:

Шаг 1: Предварительная обработка
Удалите все ненужные символы и сохраните результат в файле temp

 tr -dc 'a-zA-Z\n\t '  temp 

tr -dc 'a-zA-Z\n\t ' Это удалит все, кроме букв, \n , \t , пробел

tr ' ' '\n' Это преобразует пространство в \n чтобы отделить каждое слово в символах новой строки

Шаг 2: Обработка

 grep -wof temp <(rev temp) | sed -E -e '/^(.)\1+$/d' | awk 'length>=3 {a[$1]++} END{ for(i in a) print a[i],i; }' 

grep -wof temp <(rev temp) Это даст вам все палиндромы
-w : выберите только те строки, содержащие совпадения, которые образуют целые слова. Например: level не будет соответствовать levelAAA
-o : печать только соответствующей группы
-f : Чтобы использовать каждую строку в файле temp как шаблон для поиска в <(rev temp)

sed -E -e '/^(.)\1+$/d' : Это приведет к удалению слов, образованных из таких же букв, как AAA , BBBBB

awk 'length>=3 {a[$1]++} END{ for(i in a) print a[i],i; }' awk 'length>=3 {a[$1]++} END{ for(i in a) print a[i],i; }' : Это будет фильтровать слова length>=3 и подсчитывает их частоту и, наконец, печатает результат

Пример :

Входной файл:

 $ cat file kayak nalayak bob dad , pikachu. meow !! bhow !! 121 545 ding dong AAA BBB done kayak nalayak bob dad , pikachu. meow !! bhow !! 121 545 ding dong AAA BBB done kayak nalayak bob dad , pikachu. meow !! bhow !! 121 545 ding dong AAA BBB done 

Выход:

 $ tr -dc 'a-zA-Z\n\t '  temp $ grep -wof temp <(rev temp) | sed -E -e '/^(.)\1+$/d' | awk 'length>=3 {a[$1]++} END{ for(i in a) print a[i],i; }' 3 dad 3 kayak 3 bob 

Просто быстрая альтернатива Perl:

 perl -0nE 'for( /(\w{3,})/g ){ $a{$_}++ if $_ eq reverse($_)} END {say "$_ $a{$_}" for keys %a}' 
  • в Perl, $_ следует читать как « это ».
  • for( /(\w{3,})/g ) … для всех соответствующих слов (может потребоваться некоторая работа для отклонения ложных срабатываний типа «12a21»)
  • if $_ eq reverse($_) … если это палиндром
  • END {say "$_ $a{$_}" for...} … рассказать нам все это и его номер

\ Благодаря {sokowi, Batman}

Запуск скрипта

Сценарий ожидает, что файл будет указан как аргумент. Сценарий не читает stdin.

Удалите строку search "$1" в середине скрипта. Это не часть связанного ответа.

Сделайте исполняемый файл сценария, используя chmod u+x path/to/palindrome .

Вызовите сценарий, используя path/to/palindrome path/to/sample.txt . Если все файлы находятся в текущем рабочем каталоге, тогда команда

 ./palindrome sample.txt 

Альтернативный сценарий

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

 #! /bin/bash grep -Po '\w{3,}' "$1" | grep -Evw '(.)\1*' | sort > tmp-words grep -Fwf <(rev tmp-words) tmp-words | uniq -c rm tmp-words 

Сохраните сценарий, сделайте его исполняемым и вызовите его с файлом в качестве первого аргумента.