我经常需要找到一个文件,但我不确定该文件的名称是什么,如下所示:
$ find -iname '*foo*' -o -iname '*bar*' -o -iname '*blah*'
这有点乏味。我想创建一个更易于使用的别名,如下所示:
$ findany foo bar blah
这是我的尝试:
findany() {
args=("-iname" "'*$1*'")
shift
while [ $# -gt 0 ]; do
args+=("-o" "-iname" "'*$1*'")
shift
done
find ${args[@]}
}
问题是它永远不会产生任何结果,即使文件就在那里:
$ ls
bar.txt blah.txt foo.txt
$ findany foo bar blah
# nothing
如果我echo
在命令前面添加,它看起来是正确的:
$ findany foo bar blah
find -iname '*foo*' -o -iname '*bar*' -o -iname '*blah*'
如果我复制上面的输出并运行它,它就可以正常工作:
$ find -iname '*foo*' -o -iname '*bar*' -o -iname '*blah*'
./bar.txt
./foo.txt
./blah.txt
我认为它与参数拆分或引号有关,但我不确定如何调试它。
我通常使用 zsh,但我在 bash 中验证了相同的行为。
答案1
你们非常接近!但由于某种原因,您在文件名本身中包含了单引号,因此它们永远不会匹配。
此外,您还必须引用"${args[@]}"
才能使其正常工作。否则,它会受到分词的影响,并且任何 glob 都会在find
看到它们之前在 shell 中扩展。
试试这个(特别是对于bash
):
findany() {
local args=('-iname' "*$1*")
shift
while [ "$#" -gt 0 ]; do
args+=('-o' '-iname' "*$1*")
shift
done
find . "${args[@]}"
}
答案2
使用 zsh,您可以使用:
set -o extendedglob # usually in your ~/.zshrc, here used for (#i) for
# case insensitive matching
print -rC1 -- **/*(#i)(foo|bar|blah)*(ND)
要从数组构建该模式,您可以使用j[|]
参数扩展标志来连接数组的元素,并|
与参数扩展标志结合使用, ~
以便将其|
视为全局模式运算符,或者与~
参数扩展运算符结合使用,以便任何连接字符串中的通配符(即使是在数组元素中找到的通配符)也被视为模式。
findany1() print -rC1 -- **/*(#i)(${(~j[|])argv})*(ND)
或者:
findany2() print -rC1 -- **/*(#i)(${(j[|])~argv})*(ND)
例如findany1 '??'
会查找名称包含的文件??
,而findany2
会查找名称至少包含 2 个字符的文件。您需要findany2 '\?\?'
或findany2 '[?][?]'
查找名称??
包含findany2
.
为了与 做出同样的区分find
,那就是:
findany2() (
for arg do
argv+=( -o -iname "$arg" )
shift
done
shift
find . "$@"
)
findany1() (
set -o extendedglob
for arg do
argv+=( -o -iname "${arg//(#m)[][\\?*]/\\$MATCH}" )
shift
done
shift
find . "$@"
)
我们用( /支持的唯一引用运算符)转义find
( ?
、*
和)识别的通配符。[...]
\
\
find
fnmatch()
答案3
$ find -iname '*foo*' -o -iname '*bar*' -o -iname '*blah*'
这是解决问题的一种方法,但坦率地说,它的结果类似于
shopt -s globstar ## enable recursive globbing operator **
shopt -s extglob ## enable (|) pattern lists
shopt -s nocasematch ## take a guess!
echo **/*@(foo|bar|blah)*
(但它不需要 的帮助就可以做到这一点find
)。
我们可以很快地构建一个 shell 脚本。
#! /bin/bash -
shopt -s globstar ## enable recursive globbing operator **
shopt -s extglob ## enable (|) pattern lists
shopt -s nullglob ## don't error if nothing matches
shopt -s nocasematch ## take a guess!
IFS='|' # "$*" joins with the first character of IFS
pattern="**/*@(${*})*"
IFS= # do globbing but not splitting upon unquoted expansion:
matches=( $pattern )
for element in "${matches[@]}"; do
printf '%s\n' "${element}"
done
如果您想将其作为函数,只需将其放入pattern=…
函数done
声明中即可。