grep:显示文件名一次,然后显示带有行号的上下文

grep:显示文件名一次,然后显示带有行号的上下文

我们的源代码中到处都是错误代码。使用 grep 很容易找到它们,但我想要一个find_code可以执行的 bash 函数(例如find_code ####),它将提供以下输出:

/home/user/path/to/source.c

85     imagine this is code
86     this is more code
87     {
88         nicely indented
89         errorCode = 1111
90         that's the line that matched!
91         ok this block is ending
92     }
93 }

这是我目前拥有的:

find_code()
{
    # "= " included to avoid matching unrelated number series
    # SRCDIR is environment variable, parent dir of all of projects
    FILENAME= grep -r "= ${1}" ${SRCDIR}
    echo ${FILENAME}
    grep -A5 -B5 -r "= ${1}" ${SRCDIR} | sed -e 's/.*\.c\[-:]//g'
}

问题:

1)这不提供行号

2)它只匹配.c源文件。我无法让 sed 匹配 .c、.cs、.cpp 和其他源文件。不过,我们确实使用 C,因此简单地匹配 - 或 :(grep 在每行代码之前附加到文件名的字符)就会匹配object->pointers并弄乱一切。

答案1

我会改变一些事情。

find_code() { 
    # assign all arguments (not just the first ${1}) to MATCH
    # so find_code can be used with multiple arguments:
    #    find_code errorCode
    #    find_code = 1111
    #    find_code errorCode = 1111
    MATCH="$@" 

    # For each file that has a match in it (note I use `-l` to get just the file name
    # that matches, and not the display of the matching part) I.e we get an output of:
    #
    #       srcdir/matching_file.c
    # NOT:
    #       srcdir/matching_file.c:       errorCode = 1111
    #
    grep -lr "$MATCH" ${SRCDIR} | while read file 
    do 
        # echo the filename
        echo ${file}
        # and grep the match in that file (this time using `-h` to suppress the 
        # display of the filename that actually matched, and `-n` to display the 
        # line numbers)
        grep -nh -A5 -B5 "$MATCH" "${file}"
    done 
}

答案2

您可以使用find两个-execs,仅当第一个成功时才会执行第二个,例如仅在.cpp,.c.cs文件中搜索:

find_code() {
find ${SRCDIR} -type f \
\( -name \*.cpp -o -name \*.c -o -name \*.cs \) \
-exec grep -l "= ${1}" {} \; -exec grep -n -C5 "= ${1}" {} \;
}

因此,第一个grep打印包含您的模式的文件名,第二个将打印相应文件中的匹配行+上下文(编号)。

答案3

另一种选择:Git:

git grep --heading xxxxxxxxx

亦敬.gitignore

在 git repo 之外添加--no-index.

答案4

这是一种类似但更强大的方法,不需要解析特定的标志:

find_code {
    reset="\e[0m"
    purple="\e[35m"

    mapfile -t files < <(grep -l -R "$@" --color=none)
    for fn in "${files[@]}"; do 
        printf "$purple%s$reset\n\n" "$fn"; 
        grep "$@" "$fn"
        printf "\n"
    done
}

然而,为了完整起见,ripgrep默认输出所需的格式。

相关内容