file1.c
我有一个包含文件和file2.c
的目录file3.c
。该命令find
输出:
$find -name "*.c"
./file1.c
./file2.c
./file3.c
然后我想使用find
不带引号的.*c
.为此,我使用set -f
:
$echo $- # check current options
himBHs
$set -f
$echo $- # check that f option is set
fhimBHs
$find -name *.c
./file1.c
./file2.c
./file3.c
$set +f # unset f option
我在以下函数中尝试了相同的命令.bashrc
:
find() {
set -f
eval command find $@
set +f
}
但测试它给出了错误:
$ . ~/.bashrc && find -name *c
find: paths must precede expression: file1.c
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression
函数中出现此错误的原因是什么?find
版本:GNU 4.6.0
答案1
你没有说,但你必须像这样调用该函数:
find -name *.c
但通配符尚未关闭,因此 shell 会在*.c
调用之前展开。因此该find
命令会看到“-name”后跟三个参数,即错误消息。
您可以使用反斜杠代替引号。
find -name \*.c
答案2
您的函数禁用文件名通配符,但您使用 shell 扩展的通配符来调用它(文件名通配符是不是在您调用它的 shell 中关闭)。
换句话说,你的命令中的 glob
find -name *c
find
在调用您的函数之前展开。这会导致find
实用程序无法理解对函数内部的调用。
您可以通过使用带引号的参数调用函数来解决这个问题:
find -name "*c"
但请注意,现在该函数完全无用,因为它只是复制您已经输入的命令。
在添加到无用,也是错误的。由于$@
在代码中未加引号,因此它将在空格上分割参数(默认情况下)。这意味着你不能使用例如
find -name "* *"
查找其中包含空格的名称。
另请注意,由于 的原因eval
,shell 将对 的内容执行扩展$@
。这意味着
find -name '$(echo hello)'
找不到名为$(echo hello)
but 的文件hello
。
另一件事是,如果调用 shell已经使用set -f
,那么这将被该功能禁用。
让我们创建一个有用的函数。一个寻找a的函数数量文件名模式,例如:
myfind "*.c" "*.txt" "* *"
我们希望上面的命令返回以.c
或结尾.txt
且包含空格的路径名。
这是函数:
myfind () {
# Replace each positional parameter "$pattern"
# with -o -name "$pattern"
for pattern do
set -- "$@" -o -name "$pattern"
shift
done
shift # shift off the initial "-o"
find . "$@"
}
鉴于上述函数的调用,它将最终执行
find . -name '*.c' -o -name '*.txt' -o -name '* *'
如果您更习惯使用bash
数组作为列表,并且不介意多输入一些内容:
myfind () {
local -a args
# Build the argument list for find by adding
# -o -name "$pattern"
# for each positional parameter
for pattern do
args+=( -o -name "$pattern" )
done
args=( "${args[@]:1}" ) # remove the initial "-o"
find . "${args[@]}"
}