如何一次grep两个模式并将结果放在一个字符串中?

时间:2018-05-25 09:46:05

标签: regex bash grep

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

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 /需要高效。

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

#!/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中,我也会遇到格式问题;我需要将引号内的时间戳和文本放到一行,然后将下一个类似的匹配放到下一行。

1 个答案:

答案 0 :(得分:1)

Sed可以随时进行行选择匹配和编辑。

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

而不是

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

你可以嵌入一些空格,使其变得不那么难看而不用改变&#34; one-liner&#34;功能,循环可以取代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

注意:删除了我的测试数据,因此这项返工并未经过仔细审核。如果有人看到拼写错误,请告诉我。