递归模式搜索 - 输出格式化:对于每个匹配文件打印出特定的文件名“\n”,行号,颜色匹配“\n”的句子

递归模式搜索 - 输出格式化:对于每个匹配文件打印出特定的文件名“\n”,行号,颜色匹配“\n”的句子

我想要一个在 Linux 下的文件内容搜索命令,以便:

  1. 它将搜索指定的文件,例如:md、txt、htm。
  2. 从文件夹及其子文件夹递归,例如: .
  3. 内容搜索可以是正则表达式模式,例如:tomat.*es
  4. 它将输出匹配周围的文本
  5. 输出采用这种格式,每个文件之间用空行分隔:
file1
lineNr1:text1
lineNr2:text2

file2
lineNr1:text1
lineNr2:text2

6/最后一个标准,输出需要视觉上清晰,因此在终端上,使用 grep 等着色方案

  • 文件颜色为 color_1,例如紫色
  • color_2 中的 lineNr,例如绿色
  • 对于文本输出:
    • color_3 中的匹配文本,例如红色
    • 其余文本为 color_4,例如白色

基本上,grep 完成这项工作,但我想更改其输出格式,即:

file1:lineNr1:text1
file1:lineNr2:text2

file2:lineNr1:text1
file2:lineNr2:text2

我想要的是将注意力集中在搜索结果上,当您进行目录搜索时,搜索结果之前的文件路径名会使其更加复杂,尤其是。当文件中有多个匹配项时。我想要的是每个文件都能直接查看我正在寻找的内容。文件、子文件夹和匹配项越多,清晰的焦点就变得越重要。

因此,grep 会给出冗长的输出,从而失去焦点。也许它应该是作为 grep 命令的新功能的请求。

我已经接近我想要的了。

假设test.txt,有这2句话

2023-09-25: after colon char does not output the sentence.
2023-09-25 outputs line as there is NO colon preceding match.

然后你执行这个 cli:

grep -rwn --include=\*.{md,txt} -ie "output.*" --color=always | awk -F: '{if(f!=$1)print "\n"$1; f=$1; print $2 ":" $3;}'

在此示例中,第一行的输出在“:”处停止,而对于第二行,您有一个漂亮的输出。见附图在此输入图像描述

因此,除非匹配文本包含冒号“:”,否则此查询将完成这项工作。没有匹配周围的文本输出会使搜索输出不太有用

更复杂的示例(无法附加txt文件):

    utf-8 encoded

#        We're interested in searching on the word: tomato or tomate in french
In markdown file it can be put in bold using **tomatoes**
In a html file, content is full of tags, put a word in bold can be put in many way, such as <b>tomato</b>

Let's see what the search will return on these combinations:
1. At 6:45 will eat tomato soup.
2. Tomatoes were cooked for the soup recipe, but what time do we eat tomato soup? Isn't it six forty-five, aka 6:45?
3. Tomate en français
4. tomates: pluriel du mot tomate.
Could be tricky to restrict search only on bilingual TOMATO's variation, as for instance in automatically, there is auTOMATically.
Regular expression are of help.

假设匹配项位于 2 个子文件夹中,此 CLI 会明确说明:

grep -rn --include=\*.{md,txt} -iP "tomat[eo]s*" --color=always | awk -F: '{if(f!=$1)print "\n"$1; f=$1; print $2 ":" $3;}'

然而,附加输出冒号 char":" 之后的任何内容都无法出现在输出中,只需将冒号“:”替换为“;”你会看到差异。 在此输入图像描述

与 grep 输出进行比较 在此输入图像描述

现在,如果想将输出搜索结果转储到纯文本文件中,丢失配色方案就会丢失视觉信息。因此,带有标签的 html 文件将恢复颜色信息,可以用这样的 html 输出来完成:

<div class="grep">
<p class="grep_file">file_1</p>
<span class="grep_line">lineNr1</span>:beginning of surrounding match<span class="grep_match">SEARCH_PATTERN</span>end of surrounding match<br>
<span class="grep_line">lineNr2</span>:beginning of surrounding match<span class="grep_match">SEARCH_PATTERN</span>end of surrounding match<br>
</div>

<div class="grep">
<p class="grep_file">file_2</p>
<span class="grep_line">lineNr1</span>:beginning of surrounding match<span class="grep_match">SEARCH_PATTERN</span>end of surrounding match<br>
<span class="grep_line">lineNr2</span>:beginning of surrounding match<span class="grep_match">SEARCH_PATTERN</span>end of surrounding match<br>
</div>

通过对类进行样式设计,您可以获得您的配色方案。

现在,我尝试使用 grep 和 awk,但另一种组合可能是完成这项工作的更好主意。

谢谢

答案1

思考你想要的是这样的:

$ grep -rwn --include=\*.{md,txt} -ie "output.*" --color=always | awk -F: '{if(f!=$1){print "\n"$1;}f=$1; $1=""; }1'

file.txt
 1 2023-09-25  after colon char does not output the sentence.
 2 2023-09-25 outputs line as there is NO colon preceding match.

file1.txt
 1 2023-09-25  after colon char does not output the sentence.
 2 2023-09-25 outputs line as there is NO colon preceding match.

看起来像这样:

上述命令结果的屏幕截图,显示颜色

您的方法的问题在于您使用:作为字段分隔符,然后仅显式打印字段 2 和 3。因此,当:行中有更多字段时,您会错过其余字段。我在这里所做的是清空第一个字段 ( $1=""),然后打印整行(1;打印该行;在 中awk,当某些内容评估为 true 时,并且始终评估为 true 时,默认操作1是打印该行)。

为了清楚起见,您可以将awk代码扩展为:

awk -F: '
 {
   ## If this is a new file name, print the file name
   if ( f != $1 ){
     print "\n"$1
   }
   ## save the 1st field in the variable f
   f=$1
   ## clear the first field
   $1=""
   ## print the line
   print
}'

重要的:如果文件名本身包含:.file:weird.txt例如,您可以有一个名为的文件。处理这个问题是可能的,但需要更多的脚本,因此如果这是一个问题,请更新您的问题以包含更多示例文件名或发布新问题。

答案2

通过此命令您提供了:

grep -rwn --include=\*.{md,txt} -ie "output.*" --color=always |
    awk -F: '{if(f!=$1)print "\n"$1; f=$1; print $2 ":" $3;}'

我认为您正在尝试在以或output.*结尾的文件中查找包含大写或小写字母匹配的字符串的行。那是:.md.txt

find . -type f \( -name '*.md' -o -name '*.txt' \) -exec \
    grep -Hin 'output' \
{} +

然后,您将该输出通过管道传输到 awk,我再次认为,更改此输出:

file1:lineNr1:text1
file1:lineNr2:text2
file2:lineNr1:text1
file2:lineNr1:text2

对此:

file1
lineNr1:text1
lineNr2:text2

file2
lineNr1:text1
lineNr2:text2

因此,这就是您寻求帮助以实现打印到屏幕的内容:

$ grep -rwn --include=\*.{md,txt} -ie "output.*" --color=always |
    awk -F':' '{p=f; f=$1; sub(/[^:]+:/,"")} f!=p{print sep f; sep=ORS} 1'
test.txt
1:2023-09-25: after colon char does not output the sentence.
2:2023-09-25 outputs line as there is NO colon preceding match.

在此输入图像描述

grep但是,当读取结果时,用于对结果进行着色的 ASCII 转义序列已经存在于输出中,awk因此,如果您想生成 HTML 标签而不是 ASCII 转义序列,则需要更新 awk 脚本以在其输入中查找这些转义序列并将它们转换为 HTML 标签,这有点向后和脆弱(例如,如果原始输入中存在一些转义序列怎么办?将无法区分这些转义序列和 grep 添加的转义序列)与仅运行 awk 而不是 grep在原始输入文件上并让 awk 打印您想要的任何着色字符串。

要以您喜欢的任何布局打印未着色的文本,您不会将 find+grep 的输出通过管道传输到 awk,您可以将 grep 替换为 awk,例如

find . -type f \( -name '*.md' -o -name '*.txt' \) -exec \
    awk '
        tolower($0) ~ /output/ {
            if ( !seen[FILENAME]++ ) {
                print ORS FILENAME
            }
            print
        }
    ' \
{} +

如果您想在输出中使用颜色,请更新 awk 脚本以打印转义序列或 HTML 标记或您喜欢的任何颜色,无论您想要什么文本,请参阅https://unix.stackexchange.com/a/669122/133219https://stackoverflow.com/questions/64034385/using-awk-to-color-the-output-in-bash/64046525#64046525有关对屏幕上的颜色执行此操作的方法,请参阅https://stackoverflow.com/a/40722767/1745001https://stackoverflow.com/a/39193330/1745001了解为 HTML 输出着色的方法。

下面是在 bash 脚本中使用 find+awk 来格式化输出的示例,我认为您希望打印到屏幕上:

$ cat tst.sh
#!/usr/bin/env bash
tput sc
trap 'tput rc; exit' EXIT

colors=( reset red green yellow blue purple )
for colorNr in "${!colors[@]}"; do
    fgColorMap+=( "${colors[colorNr]} $(tput setaf $colorNr)" )
done

find . -type f \( -name '*.md' -o -name '*.txt' \) -exec \
    awk -v fgColorMap="${fgColorMap[*]}" '
        BEGIN {
            OFS = ":"
            split(fgColorMap,tmp)
            for ( i=1; i in tmp; i+=2 ) {
                fg[tmp[i]] = tmp[i+1]
            }
        }

        match(tolower($0),/output.*/) {
            if ( !seen[FILENAME]++ ) {
                if ( found++ ) { print "" }
                print fg["purple"] FILENAME fg["reset"]
            }
            print fg["green"] FNR ":" fg["reset"]                  \
                  substr($0,1,RSTART-1)                           \
                  fg["red"] substr($0,RSTART,RLENGTH) fg["reset"] \
                  substr($0,RSTART+RLENGTH)
        }
        END { if ( found ) print "" }
    ' \
{} +

这是可见的文本输出:

$ ./tst.sh
./test.txt
1:2023-09-25: after colon char does not output the sentence.
2:2023-09-25 outputs line as there is NO colon preceding match.

这是相同的,但显示了颜色代码:

$ ./tst.sh | cat -A
^[7^[[35m./test.txt^[[30m$
^[[32m1:^[[30m2023-09-25: after colon char does not ^[[31moutput the sentence.^[[30m$
^[[32m2:^[[30m2023-09-25 ^[[31moutputs line as there is NO colon preceding match.^[[30m$
$
^[8$

这是彩色输出:

![在此输入图像描述

要获取 HTML,只需更改 awk 脚本即可打印您想要的任何 HTML。您在问题中没有显示任何预期的 HTML 输出,因此我们无法帮助您获得您想要的内容,因为您没有向我们展示您想要的内容,但是有很多现有示例可供您使用(请参阅参考资料我在上面提供了),因此如果您不知道如何做到这一点,您可以稍后提出新问题。

相关内容