我想要一个在 Linux 下的文件内容搜索命令,以便:
- 它将搜索指定的文件,例如:md、txt、htm。
- 从文件夹及其子文件夹递归,例如: .
- 内容搜索可以是正则表达式模式,例如:tomat.*es
- 它将输出匹配周围的文本
- 输出采用这种格式,每个文件之间用空行分隔:
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":" 之后的任何内容都无法出现在输出中,只需将冒号“:”替换为“;”你会看到差异。
现在,如果想将输出搜索结果转储到纯文本文件中,丢失配色方案就会丢失视觉信息。因此,带有标签的 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/133219和https://stackoverflow.com/questions/64034385/using-awk-to-color-the-output-in-bash/64046525#64046525有关对屏幕上的颜色执行此操作的方法,请参阅https://stackoverflow.com/a/40722767/1745001和https://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 输出,因此我们无法帮助您获得您想要的内容,因为您没有向我们展示您想要的内容,但是有很多现有示例可供您使用(请参阅参考资料我在上面提供了),因此如果您不知道如何做到这一点,您可以稍后提出新问题。