我刚刚开始学习正则表达式,并想在任何地方使用它而不是其他正则表达式进行练习。
我在尝试查找带有扩展名的文件时遇到这种情况sh or md
$ find . regex ".*\.(sh|md)$"
.
./bogus.py
./cofollow.py
./data8.txt
./example.sh
./longest_word_2.sh
./posit_param.sh
./cobroadcast2.py
不幸的是它输出/bogus.py
,
我注意到 BRE 规则并尝试逃脱()
$ find . -regex ".*\.\(sh|md\)$"
#get nothing return
经过一系列搜索,我得到了 -regextype 解决方案正则表达式 - 查找文件
$ find . -regextype posix-extended -iregex ".*\.(sh|md)$"
./example.sh
./longest_word_2.sh
./posit_param.sh
$ find . -regextype egrep -iregex ".*\.(sh|md)$"
./example.sh
./longest_word_2.sh
./posit_param.sh
./table_regex_bat.md
此外,一个很好的模块化解决方案
$ find -type f | egrep ".*\.(sh|md)$"
./example.sh
./longest_word_2.sh
./posit_param.sh
./table_regex_bat.md
然而,BSD 中有一个快捷方式可以使用谓词来完成此类任务-E
。
$ /usr/bin/find -E . -regex ".*\.(sh|md)$"
./example.sh
./longest_word_2.sh
./posit_param.sh
我决心只使用 GNU 工具,以使我的代码和技能具有可移植性。
所以我开始别名“find -regextype egrep”,
不幸的是find获取了$1作为路径。
我怎样才能方便地解决他们的问题?
答案1
不要使用 analias
来传递参数。它们不可移植并且仅在交互式 shell 上有用。使用函数代替并传递参数作为所需的路径
regexFind() {
(( "$#" )) || { printf 'Insufficient arguments provided \n' >&2; return 1; }
find "$1" -regextype egrep -iregex ".*\.(sh|md)$"
}
并将函数调用为
regexFind "/home/foo/bar"
另外,为了补充您的发现,请注意,它bash
还有一种内在的 glob 文件方法。您只需要启用几个扩展 shell 选项即可使其工作。启用-s
该选项并-u
禁用它。
允许nullglob
忽略未扩展的全局结果作为有效匹配。因此,假设您想匹配以*.sh
and结尾的文件*.md
,您只需导航到该特定目录并执行
shopt -s nullglob
fileList=(*.sh)
fileList+=(*.md)
shopt -u nullglob
并打印结果如下所示。请记住引用扩展名以防止文件名进行分词。
printf '%s\n' "${fileList[@]}"
答案2
请注意, GNUfind
的默认正则表达式不是 BRE,而是来自某些古老版本的 GNU 的正则表达式emacs
(BRE 和 ERE 之间的某种混合体,例如,+
受支持,但您需要\(...\)
并且|
受支持,但作为\|
)。
对于 BSD find
,默认值为 BRE,您可以使用该-E
选项来启用 ERE,因此,只需执行以下操作:
alias efind='find -E'
或者:
efind() { find -E "$@"; }
在 GNU 中find
,启用 ERE 是通过-regextype posix-extended
谓词而不是选项来实现的。该谓词必须出现在文件名之后,如果存在,则必须出现在选项之后和使用它们的-regex
或之前。-iregex
GNUfind
语法是:
find [options] [files] [predicates]
^
所以你需要将其插入那里(标有 的位置^
)。
因此,在定义包装函数或脚本时,您需要考虑到这一点:跳过所有选项和文件名并在-regextype posix-extended
它们后面插入。
efind() (
found_predicate=false
for arg do
"$found_predicate" || case $arg in
(-[LPDd]|-[OD]*) ;; # skip options
(-*|['()!'])
set -- "$@" -regextype posix-extended
found_predicate=true;;
esac
set -- "$@" "$arg"
shift
done
exec find "$@"
)
其他一些注意事项:
- 您打印的第一个
bogus.py
不是因为使用了 BRE,而是因为您使用regex
了-regex
.regex
被视为文件名,而不是谓词。 find . | egrep ...
无效,因为文件路径可能由多行组成。使用 GNU 工具或兼容工具,您可以find . -print0 | grep -zE ...
处理 NUL 分隔的记录(tr '\0' '\n'
如果用于显示,则可以通过管道传输到)。
答案3
find . -type f \( -name '*.sh' -o -name '*.md' \)
这适用于 的所有实现,find
因为它不需要支持正则表达式匹配。
为了使其更加灵活:
suffixfind () (
dir=$1
shift
for suf do
set -- "$@" -o -name "*.$suf"
shift
done
shift
find "$dir" -type f \( "$@" \)
)
这个辅助 shell 函数(可以在任何sh
类似的 shell 中工作)将挑选出第一个命令行参数并将其放入变量中dir
。然后,它会-name "*.<suf1>" -o -name "*.<suf2>" (etc.)
在函数的命令行上构造一个包含所有文件名后缀的列表,然后调用find
该列表以查找 中或下的文件$dir
。
你会像这样使用它
suffixfind /usr sh md txt
查找名称以.sh
、.md
或.txt
路径中或路径下结尾的所有常规文件/usr
。
bash
使用数组和bash
局部变量对上述内容进行更详细的变体:
suffixfind () {
local dir=$1
shift
local names
names=( -name "*.$1" )
shift
for suf do
names+=( -o -name "*.$suf" )
done
find "$dir" -type f \( "${names[@]}" \)
}
关于您提到的 GNU 工具和可移植性:请注意,非 Linux 系统上的 GNU 工具有时可用,但g
工具名称带有前缀。因此, GNUfind
将可用以将其与系统上的gfind
本机实现区分开来。find
因此,您的“GNU 可移植”方法必须在测试是否实际上是 GNUgfind
之前测试是否可用。直到您完成此操作(可能通过测试返回状态和输出),您才能放心地知道您正在处理 GNU 。find
find
find --version
find