gensub на нескольких строках

gensub на нескольких строках

У меня есть файл, в котором много случайных строк, например

aaa bbb
ccc ddd
eee mark: 98 fff
ggg ggg jjjj iii
jjj kkkk

Я хочу использовать awk И только gensub для сопоставления числа "98" выше. Пока у меня есть этот код ниже, я думаю, что он не работает, потому что мне нужно заставить gensub обрабатывать "\n" как любой другой символ.

cat file.txt | awk 'printf(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}'

Мне нужно, чтобы вывод кода выше был только "98". Как это сделать?

РЕДАКТИРОВАТЬ

даже когда я использую модификатор s или m, он не работает так, как должен, поскольку, насколько мне известно, модификатор «s» должен заставить регулярное выражение обрабатывать . как любой символ, включая \n.

решение1

Вы, кажется, думаете, что awkобрабатывает свой ввод как многострочную строку. Это не так. Когда вы запускаете скрипт awk для файла, скрипт применяетсяк каждой строке файлаотдельно. Итак, ваш gensubбыл запущен один раз на строку. Вы можете делать с ним все, что хотите, awkно это действительно не лучший инструмент для работы.

Насколько я могу судить, у вас большой файл, и вы хотите напечатать только число, которое идет после mark:и пробел. Если так, то все эти подходы проще, чем возиться с gensub:

  1. Использовать grepс Perl-совместимыми регулярными выражениями ( -P)

    $ grep -oP 'mark:\s*\K\d+' file 
    98
    

    Makes печатает только совпадающую часть строки. Это конструкция PCRE, которая означает «игнорировать все совпавшее до этой точки» -o.grep\K

  2. sed

    $ sed -n 's/.*mark:\s*\([0-9]\+\).*/\1/p' file
    98
    

    Подавляет -nнормальный вывод. pВ конце sedпечатает только в том случае, если подстановка прошла успешно. Регулярное выражение само захватывает строку цифр mark:и 0 или более пробельных символов и заменяет всю строку тем, что было захвачено.

  3. Перл

    $ perl -ne 'print if s/.*mark:\s*(\d+).*/$1/' file
    98
    

    Сообщает -nPerl, что нужно прочитать входной файл построчно и применить скрипт, заданный -e. Скрипт выведет все строки, где подстановка прошла успешно.

Если вы действительно хотите использовать gensub, вы можете сделать что-то вроде:

$ awk '/mark:/{print gensub(/.*mark:\s*([0-9]+).*/,"\\1","g")}' file
98

Лично я бы сделал это в awk так:

$ awk '/mark:/{gsub(/[^0-9]/,"");print}' file
98

Поскольку вы, судя по всему, пытаетесь заставить awk принимать многострочный ввод, вот как это можно сделать (предполагая, что в вашем файле нет символов NULL):

$ awk '{print(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}' RS='\0' file
98

Устанавливает RS='\0'разделитель входных записей (именно он определяет «строку» для awk) на \0. Поскольку в вашем файле нет таких символов, это приводит к тому, что awkфайл считывается целиком сразу.

решение2

Наименьшее изменение, которое заставит это работать, будет следующим:

cat file | awk '/mark:/{printf( "%s\n",gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}'

/mark:/ — это выбор строки, содержащей «mark:».
Но тогда зачем нужен printf? Это тоже сработает:

cat file | awk '/mark:/{print(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}'

Но это было бы "бесполезное использование кота", поскольку awk может напрямую читать из файла:

awk '/mark:/{print(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}' file

Редактировать:

По запросу пользователя: Как использовать регулярное выражение для файла и строки.

Ну, с установленными вами правилами: awk только с gensub невозможен.
Кроме того, идея сопоставления с .*mark: ([0-9]+).*заменой всего этого на сопоставление внутри скобок будет означать, что для извлечения части необходимо сопоставление всего файла. Это одна из причин, по которой был создан grep.

Просто используйте:

grep -oP "mark: \K([0-9]+)" file

или:

echo "$string" | grep -oP "mark: \K([0-9]+)"

И вы получите результат.

Связанный контент