【问题标题】:How to grep two patterns at once and have the result in one string?如何一次 grep 两种模式并将结果放在一个字符串中?
【发布时间】:2018-05-26 01:20:14
【问题描述】:

我有现有的日志文件,其中包括以下类型的行:

2018-05-14T10:10:22.769029+03:00 timom usbmonitor: [INFORMATION 6] [FILE: UsbChecker.cpp:51][FUNC: vendorCheck][MSG: USB vendors changed: "0403 14e1 05e3 05e3 03f0 0403 0bda 1d6b 1d6b 1d6b 1d6b 1d6b 1d6b 1d6b" ]

从这些文件中,我想 grep 上面的行,以便从开头获取时间戳和引号内的文本,以便我有一个漂亮而紧凑的输出:

2018-05-14T10:10:22.769029+03:00 0403 14e1 05e3 05e3 03f0 0403 0bda 1d6b 1d6b 1d6b 1d6b 1d6b 1d6b 1d6b

有没有办法用单线做到这一点?
我正在寻找一种无需遍历 grepped 行即可有效获得所需输出的方法。我有数千个日志文件,每个可能有数百个匹配项,因此 grep/sed/whatever 需要高效。

到目前为止,我已经这样做了:

#!/bin/bash
INPUTDIR=
OUTPUTDIR=
while getopts ":h:d:o:" OPTION; do
    case $OPTION in
        h)
            usage
            exit 1
            ;;
        d)
            INPUTDIR=$OPTARG
            ;;
        o)
            OUTPUTDIR=$OPTARG
            ;;
        ?)
            usage
            exit 1
            ;;
    esac
done
if [ -z $INPUTDIR ] || [ -z $OUTPUTDIR ]; then
    echo "BAD ARGUMENTS: both directories aren't given" >&2
    usage
    exit 1
fi
OUTPUTFILE="$(date +%Y%m%d%H%M%S)-usb-analysis-summary"
for i in $( ls $INPUTDIR ); do
    # Interesting files are of format <number>_<number>
    if [ $(echo "$i" | grep -Ev "^[0-9]+_[0-9]+$") ] ; then
        echo "Skipping $i"
        continue
    fi
    grep vendorCheck $INPUTDIR/$i | while read -r l ; do
        # We do know timestamp is 32 characters long. GEFN
        echo "$l" | sed -r "s|^(.{32}).*changed: \"(.*)\".*|\1 \2|" >>$OUTPUTFILE
    done
done

但这不是最优的,因为现在我正在循环文件,然后从每个文件中循环 grep 匹配项。

我试过了

grep "vendorCheck" $INPUTDIR/$i | sed -r "s|^(.{32}).*changed: \"(.*)\".*|\1 \2|"

但这会删除换行符。
然后,如果我将多个模式放在一个 grep 中,我也会遇到格式化问题;我需要将引号内的时间戳和文本放在一行中,并将下一个类似的匹配项放在下一行。

【问题讨论】:

  • 你可以在这里查看,一次Grep两个模式stackoverflow.com/a/30684014/7020538
  • @AbhijithSAchal 谢谢,但这并不能回答我的问题。 grep "string1|string2" 会给我所有匹配字符串的行。我已经用 grep "vendorCheck" 对所需的行进行了 grep,并且从这些行中的每一行我想得到两个确切的模式并转储其他所有内容。

标签: regex bash grep


【解决方案1】:

Sed可以一次完成选线匹配和编辑。

你也可以使用$(...) 来生成sed 的输入文件列表,所以你真的可以把它全部放在一行中,我认为,但是ls 并不理想,你说你需要在评论中的文件名下面,所以...

而不是

sed -r -n '/vendorCheck/{s/(.{32}).*changed: \"(.*)\"/\1 \2/; p;}' $( ls -1 $INPUTDIR | egrep '^[0-9]+_[0-9]+$' ) >> $OUTPUTFILE

您可以在不更改“单行”功能的情况下嵌入一些空格以使其不那么难看,并且循环可以替换ls

for f in $INPUTDIR/[0-9]*_[0-9]* # limit input, not a definitive check
do echo "$f" | egrep '^[0-9]+_[0-9]+$' || continue # CONFIRM filename match
   [[ -f $f ]] || continue  # and assert file, not dir
   sed -r -n "/vendorCheck/{
      s/(.{32}).*changed: \"(.*)\"/\1 \2/;
      s/^/$f: /;
      p;
   }" "$f" # the "s/^/$f: /;" is a placeholder of your need for the name
done >> $OUTPUTFILE

注意:删除了我的测试数据,所以这个返工没有得到仔细审查。如果有人看到错字,请告诉我。

【讨论】:

  • 删除了我之前的评论,因为它无效。我在测试您的解决方案时拼错了路径,并认为它不起作用,但在我的路径正确后它确实如此。 :-) 最后我实际上需要文件名,所以我可能需要遍历ls $INPUTDIR,但这是另一个故事,你的回答解决了我原来的问题。非常感谢!
  • 这其实很好,因为它更容易取出ls,这最终会让你心痛。将修改...
猜你喜欢
  • 2022-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-21
  • 1970-01-01
  • 1970-01-01
  • 2017-10-25
  • 1970-01-01
相关资源
最近更新 更多