代码之家  ›  专栏  ›  技术社区  ›  Stéphane

如何使用sed、awk或gawk只打印匹配的内容?

  •  95
  • Stéphane  · 技术社区  · 16 年前

    我看到了很多关于如何使用sed、awk或gawk进行搜索和替换的例子和手册页。

    但在我的例子中,我有一个正则表达式,我想对一个文本文件运行它来提取一个特定的值。我不想搜索和替换。这是从bash调用的。让我们用一个例子:

    正则表达式示例:

    .*abc([0-9]+)xyz.*
    

    示例输入文件:

    a
    b
    c
    abc12345xyz
    a
    b
    c
    

    听起来很简单,我不知道如何正确地调用sed/awk/gawk。我希望做的是,在我的bash脚本中:

    myvalue=$( sed <...something...> input.txt )
    

    我试过的包括:

    sed -e 's/.*([0-9]).*/\\1/g' example.txt # extracts the entire input file
    sed -n 's/.*([0-9]).*/\\1/g' example.txt # extracts nothing
    
    10 回复  |  直到 9 年前
        1
  •  42
  •   mouviciel    16 年前

    我的 sed (Mac OS X)不适用于 + . 我试过 * 相反,我补充说 p 打印匹配标记:

    sed -n 's/^.*abc\([0-9]*\)xyz.*$/\1/p' example.txt
    

    用于匹配至少一个数字字符 + 我会用:

    sed -n 's/^.*abc\([0-9][0-9]*\)xyz.*$/\1/p' example.txt
    
        2
  •  31
  •   Ilia Choly    10 年前

    你可以用SED来做这个

     sed -rn 's/.*abc([0-9]+)xyz.*/\1/gp'
    
    • -n 不打印结果行
    • -r 这样做是为了不让你逃脱抓捕组帕伦斯 () .
    • \1 捕获组匹配
    • /g 全局匹配
    • /p 打印结果

    我写了一篇 tool 对我自己来说这更容易

    rip 'abc(\d+)xyz' '$1'
    
        3
  •  16
  •   fedorqui    12 年前

    我用 perl 让这对我自己更容易。例如

    perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/'
    

    它运行perl, -n 选项指示Perl一次从stdin读取一行代码并执行代码。这个 -e 选项指定要运行的指令。

    指令在read行上运行regexp,如果匹配,则打印出第一组brack的内容。( $1 )

    您可以这样做,最后也会有多个文件名。例如

    perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' example1.txt example2.txt

        4
  •  5
  •   Jim Dennis    16 年前

    如果你的版本 grep 支持你可以使用 -o 打印选项 只有 任何与regexp匹配的行的部分。

    如果没有,那么这里是最好的 sed 我可以想出:

    sed -e '/[0-9]/!d' -e 's/^[^0-9]*//' -e 's/[^0-9]*$//'
    

    ……它删除/跳过没有数字的字符,对于其余行,删除所有前导和尾随的非数字字符。(我只是猜测你的目的是从每行中提取一个数字)。

    问题在于:

    sed -e 's/.*\([0-9]*\).*/&/' 
    

    …或

    sed -e 's/.*\([0-9]*\).*/\1/'
    

    …那是 塞德 只支持“贪婪”匹配…所以第一个.*将与行的其余部分匹配。除非我们可以使用否定字符类来实现非贪婪匹配…或版本 塞德 有了Perl兼容或其他regex扩展,我们无法从模式空间(一行)中提取精确的模式匹配。

        5
  •  3
  •   fedorqui    9 年前

    你可以使用 awk 具有 match() 要访问捕获的组:

    $ awk 'match($0, /abc([0-9]+)xyz/, matches) {print matches[1]}' file
    12345
    

    这将尝试匹配模式 abc[0-9]+xyz . 如果这样做,它会将切片存储在数组中 matches ,其第一项是块 [0-9]+ . 自从 匹配() 返回子字符串起始位置的字符位置或索引(如果子字符串起始位置为1) ,它触发 print 行动。


    grep 您可以使用“向后看”和“向前看:

    $ grep -oP '(?<=abc)[0-9]+(?=xyz)' file
    12345
    
    $ grep -oP 'abc\K[0-9]+(?=xyz)' file
    12345
    

    这个检查图案 [0~9] + 当它发生在 abc xyz 只需打印数字。

        6
  •  2
  •   Mark Lakata    9 年前

    Perl是最干净的语法,但是如果您没有Perl(我理解这并不总是这样),那么使用regex的gawk和组件的唯一方法就是使用gensub特性。

    gawk '/abc[0-9]+xyz/ { print gensub(/.*([0-9]+).*/,"\\1","g"); }' < file
    

    示例输入文件的输出将

    12345
    

    注意:gensub替换整个regex(在//之间),所以需要在([0-9]+之前和之后放“.”,以便在替换中去掉数字之前和之后的文本。

        7
  •  1
  •   paxdiablo    16 年前

    如果要选择行,则去掉不需要的位:

    egrep 'abc[0-9]+xyz' inputFile | sed -e 's/^.*abc//' -e 's/xyz.*$//'
    

    它基本上选择你想要的线条 egrep 然后使用 sed 去掉数字前后的位。

    您可以在这里看到这一点:

    pax> echo 'a
    b
    c
    abc12345xyz
    a
    b
    c' | egrep 'abc[0-9]+xyz' | sed -e 's/^.*abc//' -e 's/xyz.*$//'
    12345
    pax> 
    

    更新: 显然,如果你的实际情况更复杂,资源将需要我修改。例如,如果在开始和结束时始终有一个数字埋在零或多个非数字范围内:

    egrep '[^0-9]*[0-9]+[^0-9]*$' inputFile | sed -e 's/^[^0-9]*//' -e 's/[^0-9]*$//'
    
        8
  •  -1
  •   ghostdog74    16 年前

    你可以用贝壳来做

    while read -r line
    do
        case "$line" in
            *abc*[0-9]*xyz* ) 
                t="${line##abc}"
                echo "num is ${t%%xyz}";;
        esac
    done <"file"
    
        9
  •  -3
  •   Pierre    16 年前

    对于AWK。我将使用以下脚本:

    /.*abc([0-9]+)xyz.*/ {
                print $0;
                next;
                }
                {
                /* default, do nothing */
                }
    
        10
  •  -3
  •   ghostdog74    16 年前
    gawk '/.*abc([0-9]+)xyz.*/' file