在 Linux 下写脚本时,你是不是还这样获取文件列表?
for f in *.txt; doecho"处理 $f"done
或者
ls a.txtls: cannot access a.txt: No such file or directory
但如果目录下没有任何 .txt 文件,这个循环会把字面字符串 *.txt 也当成文件名,搞出一堆 Bug。
今天介绍一个几乎没人注意的 Bash 内置命令,它能安全、优雅地按模式匹配文件名——**compgen -G**。
compgen -G 是什么
compgen 是 Bash 内部的补全生成器,而 -G 选项允许你按通配符(glob)模式列出所有匹配的文件名,就像在命令行下按 Tab 补全时看到的那样,但它的结果可以直接给程序用。
最基础的语法:
bash
compgen -G '模式'
“⚠️ 模式必须用引号括起来,否则会被 Shell 先行展开,失去通配作用。
示例 1:列出指定扩展名的文件
bash
$ compgen -G '*.sh'deploy.shtest.shbackup.sh
没有 .sh 文件?它什么也不输出,不会报错,更不会返回一个带星号的字符串。
对比 ls *.sh:文件数为 0 时会报 No such file or directory,脚本里还得额外处理。
示例 2:列出隐藏文件
bash
compgen -G '.*'
输出可能是:
text
.bashrc.gitignore.vimrc
如果想排除 . 和 .. 这两个特殊目录,可以配合 -X 过滤:
bash
compgen -G '.*' -X '\.{1,2}'
示例 3:使用字符类精细化匹配
只找 file1 到 file5,后缀不限:
bash
compgen -G 'file[1-5].*'
输出示例:
file1.txtfile3.logfile5.conf
也可以用 [a-z]、[!0-9] 等,和正常的 glob 规则完全一样。
示例 4:过滤掉不想要的文件
列出所有 .log 文件,但排除包含 _old 的:
bash
compgen -G '*.log' -X '*_old.log'
如果目录下有 app.log、system_old.log,只会输出 app.log。
示例 5:在脚本中批量处理匹配的文件
bash
for file in $(compgen -G '*.csv'); do python3 analyze.py "$file"done
即使一个 .csv 都没有,循环也不会执行,完美避免了空串或星号传入脚本的尴尬。
一个实用技巧:统计某种文件的个数
bash
count=$(compgen -G '*.png' | wc -l)echo"共找到 $count 个 PNG 图片"
无匹配时 count 为 0,逻辑干净。
记住这三点
- 永远是 Bash 内置命令,用
help compgen 查看帮助,不能用 which compgen 找。 - 模式必须加引号,单引号双引号都行,否则 Shell 会先展开成具体文件名。
- 不加路径只给文件名,如果你想得到带相对路径的列表,可以先用
cd 切到目标目录,或结合 find 使用。
写到最后
compgen -G 可能就是那种“知道之前觉得无所谓,知道之后回不去”的小命令。它没有 ls 的花哨输出,不像 find 那样笨重,在脚本里获取文件列表时,安全、安静、零副作用。