多行 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”修饰符应该使正则表达式 treat 。作为包括 \n 在内的任何字符。

答案1

您似乎认为awk将其输入视为多行字符串。事实并非如此。当您对文件运行 awk 脚本时,该脚本将被应用到文件的每一行分别地。所以,你的gensub每行运行一次。您实际上可以做您想做的事情,awk但它确实不是完成这项工作的最佳工具。

据我所知,您有一个大文件,只想打印后面的数​​字mark:和空格。如果是这样,所有这些方法都比闲逛更简单gensub

  1. grep与 Perl 兼容的正则表达式一起使用( -P)

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

    -o制造商只grep打印该行的匹配部分。这\K是一个 PCRE 结构,意思是“忽略此点之前匹配的任何内容”。

  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

编辑:

根据用户请求:如何在文件和字符串上使用正则表达式。

好吧,根据您设置的规则:仅使用 gensub 的 awk 是不可能的。
此外,匹配的想法是.*mark: ([0-9]+).*用括号内的匹配替换所有内容,这意味着需要匹配整个文件才能提取一部分。这就是创建 grep 的原因之一。

只需使用:

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

或者:

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

你就会得到结果。

相关内容