【问题标题】:searching files with grep and only outputting parts of lines使用 grep 搜索文件并仅输出部分行
【发布时间】:2017-10-02 21:23:12
【问题描述】:

我正在查看日志文件并试图在我的最终文件中获得一个不那么混乱的输出。如果我 grep 一个值,我想格式化输出以删除除日期和 url 之外的任何内容。

例如这里是文件的一行。

9 月 25 日 08:07:51 10.20.30.40 FF_STUFF[]: 1545324890 1 55.44.33.22 10.9.8.7 - 10.60.154.41 http://website.com 0 BYF 允许清洁 2 1 0 0 0 (-) 0 - 0 - 0 sqm.microsoft.com - [-] sqm.microsoft.com - - 0

如果需要,我想执行 grep 或更好的命令,以输出到仅列出粗体条目的 .txt 文件。基本上列出日期和 URL。那么我如何告诉它列出前 15 个字符,包括空格,然后找到第一个 http/https 并列出所有内容,直到第一个空格?每条线的长度或任何性质都不相同,所以我不能只按字符位置。

所以我的输出是

9 月 25 日 08:07:51 http://website.com

谢谢。

【问题讨论】:

    标签: linux bash sed grep


    【解决方案1】:

    您不能轻易在grep 中使用-o 选项,因为您有两个模式,由可变数量的字符分隔(-o 将打印完整的匹配部分)。

    如果您只想提取 URL,这就足够了:

    $ grep -oE 'https?:[^ ]+' file
    http://website.com
    

    但要同时提取日期和 URL,可能最简单的解决方案是使用 GNU awk

    $ awk '{ match($0, /https?:[^ ]+/, url); print $1, $2, $3, url[0]; }' file
    Sep 25 08:07:51 http://website.com
    

    您打印前三个字段的位置($1$3,空格分隔),然后搜索带有match() 的 URL(假设它不包含空格,即空格字符总是被正确转义;或者作为+%20),然后打印找到的第一个 URL(在日期之后)。

    如果您有 POSIX awk(或使用 --posix 标志调用 gawk),则解决方案会更加冗长,因为 POSIX match() 不支持保存将匹配的部分放入一个数组中(第三个参数,url),当找到匹配项时,您必须使用substr() 显式提取 URL:

    $ awk '{ match($0, /https?:[^ ]+/); print $1, $2, $3, substr($0, RSTART, RLENGTH); }' file
    Sep 25 08:07:51 http://website.com
    

    【讨论】:

    • 请注意,使用数组作为match() 的第三个参数是 GAWK 主义,在传统的 awk 中不起作用。 RavinderSingh13 使用 RSTART,RLENGTH 的想法是正确的,尽管他的正则表达式需要工作。
    • @ghoti,感谢您的提醒。我忘了注意解决方案仅适用于gawk。固定。
    • 一件事,当我搜索单个 /var/log/messages 时,我得到的正是我想要的。但是当我搜索所有消息文件时,因为它每周归档它们,我会在打印输出的日期前面获得额外的数据。例如,如果我在 /var/log/messages* 上运行它,我会得到“messages:Oct 1 04:05:47 website”的输出,为什么要在前面添加“messages”?
    • @Aaron_W, grep 在多个文件上运行时在前面添加文件名。要抑制该默认行为,请使用 -h/--no-filename 选项运行它。
    • @Aaron_W,另外,您可以完全避免使用grep,并在awk 中进行IP 过滤(例如awk '/8\.8\.8\.8/ { ... }' files)。
    【解决方案2】:

    为了补充@randomir的回答,我们也可以使用sed

    $ sed 's/\(.\{15\}\).*\(https\?:\/\/[^ ]\+\).*/\1 \2/' < input.txt > output.txt
    

    此模式假定前 15 个字符构成日期,并且 URL 不包含空格。它适用于 httphttps URL。


    编辑 - 解决注释——为了学习,我们还可以调用sed 来执行行匹配操作,如grep

    sed -n '/10\.45\.19\.151/p' < input.txt
    

    ...将输出 input.txt 中包含 IP 地址10.45.19.151 的所有行。 -n 选项抑制每一行的输出。我们将此选项与 p 命令结合使用以仅打印 与模式匹配的行。

    我们可以将此方法与第一个命令合并,以“grep”行使用单个命令对其进行转换:

    sed -n '/<line-match-pattern>/ s/<...>/<...>/ p' < input.txt
    

    ...将只选择匹配&lt;line-match-pattern&gt; 的行,执行替换,并输出结果。为了说明,下面是一个使用评论中提供的信息的示例:

    sed -n '/10\.45\.19\.151/ s/\(.\{15\}\).*\(https\?:\/\/[^ ]\+\).*/\1 \2/ p' \
        < messages-20171001 \
        > /backup/mikesanders-fwlog-10012017.txt
    

    【讨论】:

    • 太棒了!我首先在主日志中搜索 IP 地址,然后将其传送到您的命令以过滤掉无关数据。 grep 10.45.19.151 消息-20171001 | sed 's/(.\{15\}).*(https\?:\/\/[^ ]\+).*/\1 \2/'​​ > /backup/mikesanders-fwlog-10012017.txt
    • @Aaron_W ,作为奖励,我们还可以使用sed 代替grep。我更新了答案以显示如何使用一个命令。
    【解决方案3】:
    awk '{match($0,/http[^com]*/);print $1,$2,$3,substr($0,RSTART,RLENGTH+3)}'  Input_file
    

    以上代码说明:

    awk '{
    match($0,/http[^com]*/);                  ##Using match default utility of awk where I am searching for regex where it will look for string http till string com comes.
    print $1,$2,$3,substr($0,RSTART,RLENGTH+3)##Now printing the 1st, 2nd and 3rd column which are date and time in current line and printing sub string of current line where it should start substring from the value of RSTART till value of RLENGTH(which will be http complete URL actually). Now point to be noted here variables RSTART and RLENGTH are default variables of awk which will be set once a regex match is found in match utility of awk.
    }
    ' Input_file                              ##Mentioning the Input_file name here.
    

    【讨论】:

    • 与其将您的回答作为请求,不如将其作为回答?如果这是您建议 OP 解决他的问题的方式,那么只需包含您的代码,以及它在做什么以及为什么它比其他替代方案更好的解释。单行程序很棒,但也可以考虑将其拆分为包含 cmets 的多行程序。我们在这里的目的是进行教育,而不仅仅是让那些实际上获得报酬的顾问的生活变得困难。
    • @ghoti,对不起先生,我唯一的目的是学习和帮助人们。我通常在代码中添加解释,让我在这里添加它。另外,我的习惯是写,请你写,现在就写,再次道歉。
    • 无需道歉。我们都在这里学习,既要编码,又要更有效地交流。这是一个合理的解决方案,但还不是一个很好的答案。
    • @ghoti,我现在添加了解释,并在我的回答中删除了请求行,如果需要更多更改,请告诉我,非常乐意编辑,再次感谢。
    • 什么是/http[^com]*/?在我看来,它不太可能在做你认为你想让它做的事情。例如,试试这个:echo "http://www.example.com/foo" | grep -Eo 'http[^com]*'。由于字段由单个空格分隔,我怀疑您想要https?://[^ ]+ 的正则表达式(当然适当转义)。
    【解决方案4】:

    您可以使用grep -o 匹配您想要的每个行部分,然后重新组合 grep 返回的行:

    $ grep -Eo '^.{15}|https?://[^ ]+' f | paste - -
    Sep 25 08:07:51 http://website.com
    

    请注意,在 FreeBSD 或 OSX 中,他们使用的旧版本 GNU grep (2.5.1) 存在错误,因此需要更明确的日期识别:

    $ grep -Eo '[A-Z][a-z]{2} ([0-9]{2}[ :]){3}[0-9]{2}|https?://[^ ]+' f | paste - -
    Sep 25 08:07:51 http://website.com
    

    FreeBSD 中的一种解决方法是使用bsdgrep,它在功能上等同于 gnu grep 但没有错误。在 MacOS 中,可能需要使用 homebrew 或 macports 安装替代方案 .. 或仅在另一个答案中使用 POSIX awk 解决方案。

    无论如何,在这两种情况下,正则表达式都由两个表达式组成,它们用一个 or-bar 连接(|,在https 之前)。第一个子表达式匹配您的日期,第二个匹配您的 URL。

    只要每一行输入包含匹配这两个元素的文本,您就应该从 grep 中为每个日志条目获得两行输出。然后paste 将它们重新组合成一行。

    【讨论】:

    • 聪明的把戏,但非常脆弱——除非你有一个坚定的保证,每一行都包含日期和 URL(通常,在解析日志时,你不会这样做)。跨度>
    • @randomir 非常正确。碰巧的是,问题中样本集中的每一行似乎都适用于此。 :)
    【解决方案5】:

    只有 1 个命令行,例如:

    msr -p my.log -t "^(.*?\d+:\d+:\d+).*?(https?://\S+).*" -o '$1 $2' -PIC &gt; output.txt

    • 如果first 15 characters 比模式"^(.*?\d+:\d+:\d+)" 更可靠:

      使用"^(.{15})" 喜欢:-t "^(.{15}).*?(https?://\S+).*"

    • 如果您想进一步过滤,例如包含一个 ip 10.9.8.7 作为纯文本(-x):

      msr -p my.log -x 10.9.8.7 -t "^(.*?\d+:\d+:\d+).*?(https?://\S+).*" -o '$1 $2'

    • 如果必须包含更多的IP,如10.9.8.710.9.8.810.9.8.9,或者进一步处理:

      msr -p my.log -t "^(.*?\d+:\d+:\d+).*?(https?://\S+).*" -o '$1 $2' -PAC | msr -t "10\.9\.8\.[7-9]" -PAC &gt; output.txt

    msr.exe/msr.gcc*single exe tool 用于类似 ETL 的工作(加载 -> 提取 -> 转换或替换文件)in my open project,大约 1.6MB,无依赖关系,具有跨平台版本加上 @987654342 @/x64 版本。

    • 加载文件递归(-r)并过滤目录名、文件名、时间、大小如:

      -r -p dir1,dirN,file1,fileN -f "\.(log|txt)$" --w1 2017-09-25--nf "excluded-files" --nd "excluded-directories"--s1 1.5MB --s2 30MB--w2 "2017-09-30 22:30:50"

    • 提取 by general Regex 不同于sedawk,与C++/C#/Java/Scala 完全相同/等:

      -t "^(.*?\d+:\d+:\d+).*?(https?://\S+).*"忽略大小写:添加-ilike:-i -t-it

    • 变换输出如下:

      • -o '$1 $2' 适用于 Linux 或 Cygwin/Powershell 适用于 Windows。
      • -o "$1 $2" 用于 Windows CMD console window*.bat/*.cmd 文件。

    请看以下截图:

    如果你在 Linux 上,你可以运行 msr.gcc48msr-i386.gcc48 这是 32 位机器。只需运行 exe 即可获得用法和示例,或查看有关 performance comparison 的在线文档(使用 Linux 系统工具 grep 和 Windows 系统工具 findstr),内置在文档中,例如:msr on CentOS,丰富多彩的vivid demo on Windows

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-05-21
      • 1970-01-01
      • 2010-11-01
      • 1970-01-01
      • 2017-09-29
      • 1970-01-01
      • 2020-12-08
      相关资源
      最近更新 更多