Список фильтрации чисел, содержащих последовательные цифры

Вот мой любимый однострочный псевдоним / сценарий

    alias retry='while [ $? -ne 0 ] ; do fc -s ; done'

Затем вы можете делать что-то вроде:

     $ ps -ef | grep "Next Process"
     $ retry

, и он будет продолжать выполнять предыдущую команду, пока не найдет «Следующий процесс»

2
24.09.2018, 09:44
3 ответа

Многие большие файлы указывают на то, что это нужно сделать быстро. Это означает, что о циклеwhile readне может быть и речи . Здесь нужно понимать, что каждое из этих упражнений можно упростить, сопоставив (по крайней мере )с одним из небольшого набора шаблонов,и это можно сделать действительно быстро с помощью grepили подобных инструментов, таких как rgили ack. Например, для последовательностей из пяти цифр:

grep -e 12345 -e 23456 […] -e 65432 -e 54321

См. man grepдля получения дополнительной информации и используйте Greg's Wiki для быстрого изучения Bash.

-1
27.01.2020, 22:29

Используя grep, teeи rev, создайте небольшую хитрую функцию, полнуюbash-измов:

dqs() { a=${2:-123456789} ; [ "$1" -ge 2 ] &&  
        grep -iF "$(eval eval printf '%s\\\\n' \\$\\{a:\{0..$((${#a}-$1))\}:$1\\} |
                    tee >(rev) )"
       }

Проверь:

dqs 5 < data.log 
1234567
dqs 4 < data.log 
1234
7654
1234567
dqs 3 < data.log 
12365
349874
1234
7654
08767
1234567

Как это работает:

printfпечатает список последовательностей нужной длины, (как 123 , 234 , и т. д. ), teeдобавляет зеркало -изображение(т.е. вправо -в -влево или назад )копировать с помощью rev, затем grep -f <(...)выполняет поиск в стандартном вводе чего-либо в этом списке.

Для создания такого списка последовательностей обычно требуется цикл, или seq, или даже и то, и другое, но здесь мы обманываем, используяbashвыражение последовательностив сочетании срасширением подстроки] и немного арифметики . Но это невозможно, потому что интерпретатор bashне может выполнить их в желаемом порядке. Поэтому eval evalи несколько стратегических \\\используются, чтобы заставить bashделать что-то в правильном порядке.

[ "$@" -gt 0 ] &&здесь функционально не нужен, но безопаснее иметь его. Он гарантирует, что dqsимеет один и только один числовой параметр, иначе grepне запустится. Это предотвращает eval evalсовершение зла .

Бонус :Добавление второго аргумента может изменить 123456789на любую другую последовательность, и код все равно должен работать. Например, dqs 4 123456789ABCDEFбудет искать четырехзначные шестнадцатеричные последовательности, (и обратные последовательности ), а dqs 3 $(printf %s {a..z})будет искать трехбуквенные алфавитные последовательности.

# search `man bash` for the three most popular words 
# that have 3 three char alphabetic runs
man bash | tr ' ' '\n' | sort | uniq -c | sort -gr  | 
dqs 3 $(printf '%s' {a..z}) | head -3

Выход:

     92 first
     76 default
     38 environment
1
27.01.2020, 22:29

Если у вас много очень больших файлов, сопоставление регулярных выражений в awk будет медленным. Один из подходов состоит в том, чтобы использовать grep для выполнения тяжелой работы и awk для создания списка строк для поиска (, поскольку вы не хотите жестко кодировать эти ). т.е.

$grep -E '12|98|23|87|34|76|45|65|56|54|67|43|78|32|89|21' data.log

Делает трюк для двух символов, но мы хотим иметь возможность сделать это до 9 символов. Вам нужно -E, чтобы расширенный grep поддерживал поиск по множеству шаблонов (12|98 — это два шаблона)-обычный grep не позволяет вам это делать.

Awk может зацикливаться на строке 123456789, вытягивая последовательные части, но мы хотим двигаться вперед и назад, поэтому:

$awk 'BEGIN {f=123456789 ; b=987654321 ; for(i=1;i<9;i++) print substr(f,i,2),substr(b,i,2)}'
12 98
23 87
34 76
45 65
56 54
67 43
78 32
89 21

Давайте добавим кое-что, чтобы длина не была жестко запрограммирована на два (-vn=3 устанавливает переменную n=3 внутри awk-скрипта):

$awk -vn=3 'BEGIN {f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}'
123 987
234 876
345 765
456 654
567 543
678 432
789 321

И (почти у цели! )получить символ трубы grep -E хочет, изменив разделитель выходных записей (ORS )и разделитель выходных полей (OFS )на |

$awk -vn=3 'BEGIN {ORS="|" ; OFS="|" ; f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}'
123|987|234|876|345|765|456|654|567|543|678|432|789|321|

Мы должны избавиться от последней трубы после 321, иначе grep будет соответствовать всему, поэтому добавьте sed '.$//', чтобы заменить последний символ перед концом строки ($ )ничем:

$awk -vn=3 'BEGIN {ORS="|" ; OFS="|" ; f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}' | sed 's/.$//'

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

$cat t.sh
#!/bin/bash
grep -E `awk --assign n=$1 'BEGIN {OFS="|" ; ORS="|" ; f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}' | sed 's/.$//'` $2

$chmod 775 t.sh
$./t.sh 4 data.log
1234
7654
1234567
0
27.01.2020, 22:29

Теги

Похожие вопросы