【问题标题】:Merge lines which don't match a regex合并与正则表达式不匹配的行
【发布时间】:2017-05-09 09:28:21
【问题描述】:

我有一个包含网络日志的文件;它的简化版本如下:

en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd;
en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd;
en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd;
Unix
Linux
en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd;
START
Solaris
en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd;
Aix
SCO

我已经尝试了几个正则表达式组合来识别 Accept-Language,它是使用以下 awk/sed 的每一行的开头:

/^[a-z]{2}(-[A-Z]{2})?/
/\*|[A-Z]{1,8}(-[A-Z0-9]{1,8})*/i  
/([^-;]*)(?:-([^;]*))?(?:;q=([0-9]\.[0-9]))?/

到目前为止,我还没有设法让 awk/sed 给我以下结果:

en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd;
en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd;
en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd;
en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd;    Unix    Linux
en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd;    STAR    Solaris
en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd;    Aix    SCO

感谢任何帮助。该文件包含大约 100 万条以上的记录,因此我很高兴采用不使用 sed/awk 并提高性能的路线。

【问题讨论】:

  • 在你想要的输出中,我相信你有多余的一行。删除前三个中的一个?

标签: regex file awk sed http-accept-language


【解决方案1】:

只是为了好玩,这里有一个 sed 解决方案:

sed -ne 1bgo \
   -e '/^[a-z][a-z]-[A-Z][A-Z]/ { x;p;s/.*//;x; };:go' \
   -e 'H;x;s/^\n//;s/\n/  /;x;${ x;p; }' < input

它是这样工作的:

  • 读取每一行,但不是立即打印,而是通过将其附加到保留空间 (H) 来保存它,除了删除任何将它与已经存在的内容分开的换行符 (x;s/^\n//;s/\n/ /;x)。 (如果你想在你的输出中使用标签,把它们放在我已经放了几个空格的地方。)

  • 如果遇到与接受语言模式匹配的行,请在向其附加任何内容之前刷新保留空间。打印并清除它 (x;p;s/.*//;x)。然后像往常一样进行追加和诸如此类的事情。

  • 将第一行和最后一行与其他所有行区别对待:永远不要在仅读取第一行后刷新保持空间(1bgo 跳过该位置,向下到标记为 :go 的位置),并且始终刷新读完最后一行后保留空格 (${ x;p; })

【讨论】:

  • 这也很完美,我应该补充一下!唯一的问题是处理更大规模的数据需要很长时间(尤其是使用 Mapreduce)。感谢您的回复
【解决方案2】:

根据观察,我们可以区分=上的两种类型的行,你可以使用这个awk脚本:

file.awk

$0 ~ /=/ { printf("%s%s", v,$0)
           v="\n"
           next
         } 
         { printf("\t%s", $0) } 
END      { printf("\n") }

你可以这样使用它:awk -f file.awk yourfile

  • v 第一行为空,后面包含换行符
  • 对于带有= 的行,我们打印$0 前面加上v
  • 对于其他行(注意第一个操作中的next),我们打印$0,不带换行符,但使用\t 作为分隔符

【讨论】:

  • 不要用 printf("\n") 硬编码换行字符串,而是使用 print "" 以便 awk 可以使用任何适合调用它的环境的换行字符串,例如\r\n。此外,您不需要 $0 ~,因为这是默认设置。
【解决方案3】:
$ awk '/[a-z]{2}-[A-Z]{2}/ { print b; b=$0; next }  # @xx-XX empty buffer, refill
                           { b=b OFS $0 }           # otherwise append to buffer
                       END { print b }' file        # dump the buffer in the end

en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd;
en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd;
en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd; Unix Linux
en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd; START Solaris
en-GB,en-US;q=0.8,en    jsdjpksdkskd;lkskd; Aix SCO

你会得到一个空行来开始输出。此外,如果需要,请在输出上使用制表符分隔符:awk -v OFS="\t" ...

【讨论】:

  • 这个脚本对我不起作用,它将所有行合二为一。
  • @AmineJaidi 这很奇怪。你的环境是什么,你用的是哪个 awk?
  • 我在 Redhat 上没有使用 GAWK。问题是文件已经被 \t 分隔,基本上我在这里要解决的问题是确保所有不以正则表达式 Accept-language 开头的行都附加到前一行。下面的 sed 解决方案正在发挥作用,如果知道 AWK 是如何做到这一点的,那就太好了我已经将 SED 解决方案作为 Hadoop 中 reduce 函数的一部分实现了,而且速度很慢
  • 首先想到的是您的 awk 不支持正则表达式中的{2}。将正则表达式:/[a-z]{2}-[A-Z]{2} 替换为 /[a-z][a-z]-[A-Z][A-Z]
  • 现在脚本可以工作了,但它在文件开头添加了一个 \n :(
猜你喜欢
  • 2016-02-16
  • 1970-01-01
  • 2013-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-08
  • 1970-01-01
  • 2013-10-28
相关资源
最近更新 更多