在函数内部设置 -f

在函数内部设置 -f

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[@]}"
}

相关内容