![gensub на нескольких строках](https://rvso.com/image/76424/gensub%20%D0%BD%D0%B0%20%D0%BD%D0%B5%D1%81%D0%BA%D0%BE%D0%BB%D1%8C%D0%BA%D0%B8%D1%85%20%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B0%D1%85.png)
У меня есть файл, в котором много случайных строк, например
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
:
Использовать
grep
с Perl-совместимыми регулярными выражениями (-P
)$ grep -oP 'mark:\s*\K\d+' file 98
Makes печатает только совпадающую часть строки. Это конструкция PCRE, которая означает «игнорировать все совпавшее до этой точки»
-o
.grep
\K
sed
$ sed -n 's/.*mark:\s*\([0-9]\+\).*/\1/p' file 98
Подавляет
-n
нормальный вывод.p
В концеsed
печатает только в том случае, если подстановка прошла успешно. Регулярное выражение само захватывает строку цифрmark:
и 0 или более пробельных символов и заменяет всю строку тем, что было захвачено.Перл
$ perl -ne 'print if s/.*mark:\s*(\d+).*/$1/' file 98
Сообщает
-n
Perl, что нужно прочитать входной файл построчно и применить скрипт, заданный-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]+)"
И вы получите результат.