【问题标题】:Using grep to get the next WORD after a match in each line在每行匹配后使用 grep 获取下一个 WORD
【发布时间】:2012-06-13 20:46:49
【问题描述】:

我想从我的服务器日志中获取“GET”查询。

例如,这是服务器日志

1.0.0.127.in-addr.arpa - - [10/Jun/2012 15:32:27] code 404, message File not fo$
1.0.0.127.in-addr.arpa - - [10/Jun/2012 15:32:27] "GET /hello HTTP/1.1" 404 -   
1.0.0.127.in-addr.arpa - - [10/Jun/2012 15:41:57] code 404, message File not fo$
1.0.0.127.in-addr.arpa - - [10/Jun/2012 15:41:57] "GET /ss HTTP/1.1" 404 -

当我尝试使用简单的 grep 或 awk 时,

Adi:~ adi$ awk '/GET/, /HTTP/' serverlogs.txt

它给出了

1.0.0.127.in-addr.arpa - - [10/Jun/2012 15:32:27] "GET /hello HTTP/1.1" 404 -
1.0.0.127.in-addr.arpa - - [10/Jun/2012 15:41:57] "GET /ss HTTP/1.1" 404 -

我只想显示:helloss

有什么办法可以做到吗?

【问题讨论】:

    标签: linux grep


    【解决方案1】:

    在这种情况下,由于日志文件具有已知结构,一种选择是使用cut 拉出第 7 列(字段默认由制表符表示)。

    grep GET log.txt | cut -f 7 
    

    【讨论】:

    • 仍然显示整行。 1.0.0.127.in-addr.arpa - - [10/Jun/2012 15:32:27] “GET /hello HTTP/1.1”404 - 1.0.0.127.in-addr.arpa - - [10/Jun/2012 15:41:57]“GET /ss HTTP/1.1”404 -
    • 嗯,是空格还是制表符分隔?如果是空格,使用-d' ' with cut 指定空格作为列分隔符。
    • -d ' ' 参数配合使用效果很好。
    【解决方案2】:

    使用管道通常比使用单个复杂的正则表达式更容易。这适用于您提供的数据:

    fgrep GET /tmp/foo | 
        egrep -o 'GET (.*) HTTP' |
        sed -r 's/^GET \/(.+) HTTP/\1/'
    

    此管道返回以下结果:

    hello
    ss
    

    当然还有其他方法可以完成这项工作,但这显然适用于提供的语料库。

    【讨论】:

      【解决方案3】:

      假设你有 gnu grep,你可以使用 perl 风格的正则表达式来做一个积极的向后看:

      grep -oP '(?<=GET\s/)\w+' file
      

      如果你没有 gnu grep,那么我建议只使用 sed:

      sed -n '/^.*GET[[:space:]]\{1,\}\/\([-_[:alnum:]]\{1,\}\).*$/s//\1/p' file
      

      如果你碰巧有gnu sed,那可以大大简化:

      sed -n '/^.*GET\s\+\/\(\w\+\).*$/s//\1/p' file
      

      这里的底线是,您当然不需要管道来完成此操作。 grepsed 就足够了。

      【讨论】:

      • Sed 像 /s/one/two/g 一样工作,有 4 个斜线。使用/^.*GET\s\+\/\(\w\+\).*$/s//\1/p,您有 5 个斜线和s之间。 s 这里是什么?
      • @Timo What is s here? s 有一个 sed 替换命令。 /regex/ 是一个匹配命令 - 只有当行匹配时,它才会在 /regex/ 之后执行命令。所以s 命令只有在/^.*GET\s\+\/\(\w\+\).*$/ mathces 时才会执行。然后空的正则表达式重用最后一个正则表达式,所以s// 等于s/^.*GET\s\+\/\(\w\+\).*$/。它被\1 取代,因此被第一个匹配组所取代,即。 \(\w\+\)。如果s 命令成功,s 命令的p 标志会导致打印替换。有关详细信息,请参阅grymoire.com/Unix/Sed.html 和 posix sed 手册。
      【解决方案4】:

      如果您使用 grep,请使用管道:

      grep -o /he.* log.txt | grep -o [^/].*
      grep -o /ss log.txt | grep -o [^/].*
      

      [^/] 表示从 grep 输出中提取 ^ 符号后面的字母

      【讨论】:

        【解决方案5】:
        gawk '{match($7,/\/(\w+)/,a);} length(a[1]){print a[1]}' log.txt
        hello
        ss
        

        如果您有gawk,那么上面的命令将使用match 函数使用正则表达式选择所需的值并将其存储到数组a

        【讨论】:

          【解决方案6】:

          我试图这样做并遇到了这个链接:https://www.unix.com/shell-programming-and-scripting/153101-print-next-word-after-found-pattern.html

          总结: 使用 grep 查找匹配行,然后使用 awk 查找模式并打印下一个字段:

          grep pattern logfile | \
            awk '{for(i=1; i<=NF; i++) if($i~/pattern/) print $(i+1)}'
          

          如果你想知道独特的出现:

          grep pattern logfile | \
            awk '{for(i=1; i<=NF; i++) if($i~/pattern/) print $(i+1)}' | \
            sort | \
            uniq -c
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-09-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-12-07
            • 2020-03-03
            相关资源
            最近更新 更多