Gemini cli搜索文件find、grep的例子

一、find . -type f -name ".cpp" -o -name " .hpp" -o -name ".h" -o -name ".c" | xargs ls -lt | head -n 20

这条命令的作用大致是:

在当前目录及子目录中查找 C/C++ 源码文件,然后按修改时间从新到旧排序,显示最新的 20 个文件。


1. find .

bash 复制代码
find .

表示从当前目录 . 开始递归查找。

例如当前目录下面有:

text 复制代码
.
├── main.cpp
├── include/foo.h
└── src/bar.c

find . 会遍历这些文件和目录。


2. -type f

bash 复制代码
-type f

表示只匹配普通文件,排除目录、软链接、设备文件等。


3. -name "*.cpp" -o -name "*.hpp" ...

bash 复制代码
-name "*.cpp" -o -name "*.hpp" -o -name "*.h" -o -name "*.c"

这里是在匹配文件名:

  • *.cpp:C++ 源文件
  • *.hpp:C++ 头文件
  • *.h:C/C++ 头文件
  • *.c:C 源文件

其中:

bash 复制代码
-o

表示逻辑"或"。

所以意图是:

text 复制代码
名字是 .cpp 或 .hpp 或 .h 或 .c

4. | xargs ls -lt

bash 复制代码
| xargs ls -lt

前面的 find 会输出匹配到的文件路径,例如:

text 复制代码
./main.cpp
./src/foo.c
./include/bar.h

然后通过管道 | 传给 xargs

xargs ls -lt 会把这些文件作为参数传给:

bash 复制代码
ls -lt 文件1 文件2 文件3 ...

其中:

bash 复制代码
ls -lt

含义是:

  • -l:长格式显示,包括权限、所有者、大小、时间等
  • -t:按修改时间排序,新的在前面

5. | head -n 20

bash 复制代码
| head -n 20

只显示前 20 行。

结合 ls -lt,就是显示修改时间最新的 20 个文件。


命令的意图

bash 复制代码
find 当前目录下所有 .cpp/.hpp/.h/.c 文件
然后按修改时间从新到旧排序
最后显示最新的 20 个

但这条命令有一个重要问题

原命令:

bash 复制代码
find . -type f -name "*.cpp" -o -name "*.hpp" -o -name "*.h" -o -name "*.c"

find 里,-o 的优先级容易导致结果不是你想的那样。

它实际更接近于:

bash 复制代码
find . \( -type f -name "*.cpp" \) -o -name "*.hpp" -o -name "*.h" -o -name "*.c"

也就是说,-type f 只严格作用到了 *.cpp,后面的 *.hpp*.h*.c 不一定都受 -type f 限制。

更稳妥的写法应该加括号:

bash 复制代码
find . -type f \( -name "*.cpp" -o -name "*.hpp" -o -name "*.h" -o -name "*.c" \) | xargs ls -lt | head -n 20

注意括号前面要加反斜杠 \(\),避免被 shell 解释。


更推荐的安全写法

如果文件名里可能有空格、换行等特殊字符,原来的 xargs 可能会出问题。

更安全的写法是:

bash 复制代码
find . -type f \( -name "*.cpp" -o -name "*.hpp" -o -name "*.h" -o -name "*.c" \) -print0 | xargs -0 ls -lt | head -n 20

这里:

bash 复制代码
-print0

用空字符分隔文件名。

bash 复制代码
xargs -0

按空字符读取文件名。

这样可以正确处理类似这种文件名:

text 复制代码
./src/my file.cpp

小结

原命令的作用是找出最近修改的 C/C++ 文件:

bash 复制代码
find . -type f -name "*.cpp" -o -name "*.hpp" -o -name "*.h" -o -name "*.c" | xargs ls -lt | head -n 20

但更推荐改成:

bash 复制代码
find . -type f \( -name "*.cpp" -o -name "*.hpp" -o -name "*.h" -o -name "*.c" \) -print0 | xargs -0 ls -lt | head -n 20

这样逻辑更准确,也更能处理复杂文件名。

二、find . -type f ( -name ".cpp" -o -name " .hpp" -o -name ".h" -o -name " .c" -o -name ".sh" -o -name "Makefile " ) -not -path "/." -not -name │ │

│ │ "comment_by_audit.txt" -exec grep -rnE "(//|/*|#).*\s+by\s+" {} + | tee comment_by_audit.txt | wc -l

这条命令的作用可以概括为:

在当前目录下递归查找 C/C++、Shell、Makefile 等源码文件,排除隐藏目录/隐藏文件以及 comment_by_audit.txt,然后在这些文件中搜索类似"注释里包含 by 某人"的行,把匹配结果写入 comment_by_audit.txt,同时统计匹配行数。

命令如下,整理一下格式:

bash 复制代码
find . -type f \
  \( -name "*.cpp" -o -name "*.hpp" -o -name "*.h" -o -name "*.c" -o -name "*.sh" -o -name "Makefile*" \) \
  -not -path "*/.*" \
  -not -name "comment_by_audit.txt" \
  -exec grep -rnE "(//|/\*|#).*\s+by\s+" {} + \
| tee comment_by_audit.txt \
| wc -l

下面逐段解释。


1. find . ## 2. -type f

3. 文件类型过滤

bash 复制代码
\( -name "*.cpp" -o -name "*.hpp" -o -name "*.h" -o -name "*.c" -o -name "*.sh" -o -name "Makefile*" \)

这一段表示根据文件名查找:

  • C++ 源文件:*.cpp
  • C++ 头文件:*.hpp
  • C/C++ 头文件:*.h
  • C 源文件:*.c
  • Shell 脚本:*.sh
  • Makefile 文件:MakefileMakefile.xxx
bash 复制代码
-o

表示逻辑"或"。

括号:

bash 复制代码
\( ... \)

是为了把这些 -name 条件组合起来,避免 find 的逻辑优先级导致匹配错误。


4. 排除隐藏路径

bash 复制代码
-not -path "*/.*"

这一段表示排除路径中包含隐藏目录或隐藏文件的内容。

例如这些会被排除:

text 复制代码
./.git/config
./.vscode/settings.json
./src/.cache/foo.c

因为它们的路径里包含:

text 复制代码
/. 

也就是某一级目录或文件名以 . 开头。

这个条件通常用于跳过:

text 复制代码
.git
.svn
.cache
.vscode
.idea

等隐藏目录。


5. 排除输出文件本身

bash 复制代码
-not -name "comment_by_audit.txt"

表示排除名为:

text 复制代码
comment_by_audit.txt

的文件。

这是很重要的,因为后面会把结果写入这个文件。如果不排除它,下一次执行命令时可能会把上一次的结果文件也拿来搜索,造成污染或重复匹配。


6. 对找到的文件执行 grep

bash 复制代码
-exec grep -rnE "(//|/\*|#).*\s+by\s+" {} +

这一段是核心。

-exec ... {} +

bash 复制代码
-exec command {} +

表示对 find 找到的文件执行某个命令。

这里执行的是:

bash 复制代码
grep -rnE "(//|/\*|#).*\s+by\s+" 文件列表

其中:

bash 复制代码
{}

代表 find 找到的文件。

bash 复制代码
+

表示把多个文件尽量合并成一次 grep 调用,而不是每个文件执行一次。这样效率更高。


7. grep -rnE 的含义

bash 复制代码
grep -rnE

分别表示:

-r

递归搜索。

不过这里其实有点多余,因为文件已经由 find 找出来了,传给 grep 的是具体文件。

-n

显示匹配行的行号。

输出大概类似:

text 复制代码
./src/main.cpp:42:// modified by Alice

其中 42 就是行号。

-E

使用扩展正则表达式。

这样可以使用更方便的正则语法,比如:

regex 复制代码
a|b

表示匹配 ab


8. 正则表达式解释

regex 复制代码
(//|/\*|#).*\s+by\s+

这段正则用于查找"注释里出现 by"的行。

拆开看:

(//|/\*|#)

匹配三种注释开头:

regex 复制代码
//

C/C++ 单行注释,例如:

cpp 复制代码
// changed by Tom
regex 复制代码
/\*

C/C++ 块注释开头,例如:

cpp 复制代码
/* created by Jerry */

这里写成:

regex 复制代码
/\*

是因为 * 在正则中有特殊含义,需要用反斜杠转义。

regex 复制代码
#

Shell 或 Makefile 注释,例如:

bash 复制代码
# updated by Alice

.*

表示注释符号后面可以有任意字符。

例如:

cpp 复制代码
// TODO: fixed by Bob

\s+by\s+

表示匹配:

text 复制代码
空白字符 + by + 空白字符

也就是说,它希望 by 是一个独立单词,而不是别的单词的一部分。

会匹配:

cpp 复制代码
// fixed by Alice

不会匹配:

cpp 复制代码
// standby mode

因为 standby 中的 by 前面不是空白,后面也不一定是空白。


9. tee comment_by_audit.txt

bash 复制代码
| tee comment_by_audit.txt

tee 的作用是:

  1. 把前面 grep 的输出显示到终端;
  2. 同时写入文件 comment_by_audit.txt

也就是说,匹配结果会保存到:

text 复制代码
comment_by_audit.txt

如果这个文件已存在,会被覆盖。

例如输出内容可能是:

text 复制代码
./src/main.cpp:42:// modified by Alice
./scripts/build.sh:10:# generated by build system
./Makefile:5:# written by Bob

这些内容会同时出现在终端和 comment_by_audit.txt 中。


10. wc -l

bash 复制代码
| wc -l

统计前面输出的行数。

因为前面是 grep 匹配结果,所以这里统计的是:

匹配到的注释行数量。

例如如果有 37 行注释匹配了这个模式,最终输出:

text 复制代码
37

整条命令的最终效果

这条命令会查找类似下面的内容:

cpp 复制代码
// modified by Alice
cpp 复制代码
/* created by Bob */
c 复制代码
// added by John
bash 复制代码
# generated by script
makefile 复制代码
# maintained by team

然后生成一个报告文件:

text 复制代码
comment_by_audit.txt

里面记录所有匹配位置,例如:

text 复制代码
./src/main.cpp:42:// modified by Alice
./include/foo.h:18:/* created by Bob */
./build.sh:7:# generated by build system
./Makefile:12:# maintained by build team

最后终端会输出匹配总行数。


一个小问题:grep -r 在这里不太必要

因为前面 find 已经递归找文件了:

bash 复制代码
find . ...

后面的 grep 接收到的是具体文件列表,所以:

bash 复制代码
grep -rnE

里的 -r 可以省略。

更简洁:

bash 复制代码
find . -type f \
  \( -name "*.cpp" -o -name "*.hpp" -o -name "*.h" -o -name "*.c" -o -name "*.sh" -o -name "Makefile*" \) \
  -not -path "*/.*" \
  -not -name "comment_by_audit.txt" \
  -exec grep -nE "(//|/\*|#).*\s+by\s+" {} + \
| tee comment_by_audit.txt \
| wc -l

另一个注意点:\s 的兼容性

你的正则里用了:

regex 复制代码
\s

在很多 GNU grep -E 环境中可以工作,但严格来说,POSIX ERE 里更标准的空白写法是:

regex 复制代码
[[:space:]]

所以更兼容的写法是:

bash 复制代码
find . -type f \
  \( -name "*.cpp" -o -name "*.hpp" -o -name "*.h" -o -name "*.c" -o -name "*.sh" -o -name "Makefile*" \) \
  -not -path "*/.*" \
  -not -name "comment_by_audit.txt" \
  -exec grep -nE "(//|/\*|#).*[[:space:]]+by[[:space:]]+" {} + \
| tee comment_by_audit.txt \
| wc -l

这个版本更适合跨 Linux/macOS/Unix 环境使用。


再注意:它只匹配小写 by

当前命令只会匹配:

text 复制代码
by

不会匹配:

text 复制代码
By
BY

如果你想忽略大小写,可以加 -i

bash 复制代码
grep -niE "(//|/\*|#).*[[:space:]]+by[[:space:]]+"

完整版本:

bash 复制代码
find . -type f \
  \( -name "*.cpp" -o -name "*.hpp" -o -name "*.h" -o -name "*.c" -o -name "*.sh" -o -name "Makefile*" \) \
  -not -path "*/.*" \
  -not -name "comment_by_audit.txt" \
  -exec grep -niE "(//|/\*|#).*[[:space:]]+by[[:space:]]+" {} + \
| tee comment_by_audit.txt \
| wc -l

总结一句话

这条命令是在做一次"注释审计":

扫描当前工程中的 C/C++、Shell、Makefile 文件,找出注释里包含类似 by xxx 的行,保存到 comment_by_audit.txt,并统计总共有多少条匹配。

三、grep "@" comment_by_audit.txt > temp_audit.txt && mv temp_audit.txt comment_by_audit.txt && wc -l comment_by_audit.txt

这条命令的作用可以概括为:

comment_by_audit.txt 中筛选出包含 @ 的行,覆盖原来的 comment_by_audit.txt,然后统计剩余行数。

命令是:

bash 复制代码
grep "@" comment_by_audit.txt > temp_audit.txt && mv temp_audit.txt comment_by_audit.txt && wc -l comment_by_audit.txt

逐段解释

1. grep "@" comment_by_audit.txt

bash 复制代码
grep "@" comment_by_audit.txt

表示在文件 comment_by_audit.txt 中查找包含 @ 的行。

例如原文件内容是:

text 复制代码
./src/a.cpp:10:// modified by alice@example.com
./src/b.cpp:20:// modified by Bob
./src/c.cpp:30:# generated by build@system

执行后会筛出包含 @ 的行:

text 复制代码
./src/a.cpp:10:// modified by alice@example.com
./src/c.cpp:30:# generated by build@system

这通常是在进一步筛选包含邮箱地址或类似账号标识的审计结果。


2. > temp_audit.txt

bash 复制代码
> temp_audit.txt

表示把 grep 的输出重定向到临时文件:

text 复制代码
temp_audit.txt

也就是说,不直接显示到终端,而是写入这个临时文件。

为什么不直接写回原文件?

不要这样写:

bash 复制代码
grep "@" comment_by_audit.txt > comment_by_audit.txt

因为 shell 会先清空 comment_by_audit.txt,然后 grep 再去读它,结果可能导致原文件内容丢失。

所以这里使用临时文件:

bash 复制代码
grep "@" comment_by_audit.txt > temp_audit.txt

然后再用 mv 覆盖原文件。


3. &&

bash 复制代码
&&

表示"前一个命令成功后,才执行下一个命令"。

所以这里的执行逻辑是:

text 复制代码
如果 grep 成功生成 temp_audit.txt
才执行 mv

如果 mv 成功覆盖 comment_by_audit.txt
才执行 wc -l

4. mv temp_audit.txt comment_by_audit.txt

bash 复制代码
mv temp_audit.txt comment_by_audit.txt

表示把临时文件重命名为:

text 复制代码
comment_by_audit.txt

如果原来的 comment_by_audit.txt 已经存在,会被覆盖。

执行后,comment_by_audit.txt 里就只剩下包含 @ 的行。


5. wc -l comment_by_audit.txt

bash 复制代码
wc -l comment_by_audit.txt

统计文件的行数。

输出类似:

text 复制代码
25 comment_by_audit.txt

表示筛选后文件中还有 25 行。


整条命令的执行流程

可以理解成:

text 复制代码
1. 从 comment_by_audit.txt 里找出包含 @ 的行
2. 把这些行写入 temp_audit.txt
3. 如果第 1 步成功,则用 temp_audit.txt 覆盖 comment_by_audit.txt
4. 如果覆盖成功,则统计新的 comment_by_audit.txt 有多少行

举个例子

假设原来的 comment_by_audit.txt 是:

text 复制代码
./main.cpp:12:// changed by alice@example.com
./util.c:33:// fixed by Bob
./build.sh:8:# generated by ci@company.com
./Makefile:5:# updated by release team

执行:

bash 复制代码
grep "@" comment_by_audit.txt > temp_audit.txt && mv temp_audit.txt comment_by_audit.txt && wc -l comment_by_audit.txt

筛选后,新的 comment_by_audit.txt 变成:

text 复制代码
./main.cpp:12:// changed by alice@example.com
./build.sh:8:# generated by ci@company.com

终端输出:

text 复制代码
2 comment_by_audit.txt

一个重要注意点

这条命令有一个细节:如果 grep "@" comment_by_audit.txt 没有找到任何包含 @ 的行,grep 的退出码通常是 1

因为用了 &&,所以后面的:

bash 复制代码
mv temp_audit.txt comment_by_audit.txt

不会执行。

结果是:

  • temp_audit.txt 可能会被创建成空文件;
  • 原来的 comment_by_audit.txt 不会被覆盖;
  • wc -l 也不会执行。

也就是说,如果没有匹配项,原文件会保持不变。


如果你希望"没有匹配项时也清空原文件"

可以改成:

bash 复制代码
grep "@" comment_by_audit.txt > temp_audit.txt; mv temp_audit.txt comment_by_audit.txt; wc -l comment_by_audit.txt

或者更安全一点:

bash 复制代码
grep "@" comment_by_audit.txt > temp_audit.txt || true
mv temp_audit.txt comment_by_audit.txt
wc -l comment_by_audit.txt

这样即使没有任何包含 @ 的行,comment_by_audit.txt 也会被更新为空文件。


总结一句话

这条命令是在对之前的审计结果做二次过滤:

只保留 comment_by_audit.txt 中包含 @ 的匹配行,用过滤结果覆盖原文件,然后输出过滤后的总行数。

相关推荐
冷色调的咖啡师1 小时前
1.大数据架构技术 上——搭建分布式Hadoop集群
大数据·linux·hadoop·分布式·hdfs·架构·yarn
信创工程师-小杨1 小时前
OpenEuler系统如何升级OpenSSh10.3P1版本
linux·运维·服务器
2601_961194021 小时前
27考研刘晓燕资源
linux·sql·ubuntu·华为·pdf·.net
yyuuuzz1 小时前
运维场景中AI模型的常见问题
运维·服务器·人工智能·深度学习·机器学习·云计算·aws
拾光Ծ3 小时前
【Linux网络】计算机网络入门:网络产生与协议
linux·网络·网络协议·tcp/ip·计算机网络
稳联技术老娜10 小时前
DeviceNet主站怎么连接西门子PLC,Profinet网关配置手册(那智机器人)
服务器·网络·数据库
9分钟带帽10 小时前
linux_系统开机自动执行shell脚本
linux·服务器
消失在人海中11 小时前
oracle 数据库多表关联查询
服务器·数据库·oracle
嵌入式小能手12 小时前
飞凌嵌入式ElfBoard-进程间的通信之命名管道
linux·服务器·算法