for循环范围中通配符扩展的问题

for循环范围中通配符扩展的问题

我制作的 bash 脚本遇到问题。在 for 循环中,我迭代所有参数来构造一个字符串变量,该变量稍后将输入到“eval”命令:

    for arg in "$*"
    do
        if [ $arg != $lastArg ]; then
            findTarget+="-name $arg -o "
        else
            findTarget=$(echo $findTarget | sed 's/-o$//')
            break
        fi
    done

问题源于“$*”。例如,当我在参数中输入“*.c”,并且当前文件夹包含与该模式匹配的文件时,参数*.c将扩展为这些文件;我不希望那样,我想findTarget与 相连-name *.c -o,我尝试过使用 eval 并没有引号,似乎没有任何效果。知道如何做到这一点(如果可能的话,简单地)?注意:参数总数可能会有所不同。

这是我如何运行脚本的示例:

$ trouver.bash *.c *.f90 someString

在我的 for 循环结束时,变量findTarget应该读取-name *.c -o -name *.f90

*.c如果或*.f90匹配当前文件夹中的文件,则此操作不起作用...

答案1

循环位置参数的语法是for arg do单独的。"$*"是位置参数与第一个字符的串联$IFS,因此您将仅循环一个元素。

另外,如果您想为命令构建参数列表find,则需要一个数组,而不是字符串。并且不要忘记引用您的变量!

所以:

findTarget=()
or=()
for arg
do
    [ "$arg" = "$lastArg" ] && break
    findTarget+=("${or[@]}" -name "$arg")
    or=(-o)
done

find . \( "${findTarget[@]}" \)

请注意,当您调用脚本时,您需要引用...模式*.c,否则它们将在传递给脚本之前由 shell 扩展。

trouver.bash '*.c' '*.f90' someString

如果您的交互式 shell 是zsh,您可以为禁用通配符的命令定义一个别名:

alias trouver.bash='noglob trouver.bash'

这样,您可以执行以下操作:

trouver.bash *.c *.f90 someString

没有外壳扩展这些*.c *.f90球体。

答案2

使用"$*"方法将所有参数连接成一个长参数。将其更改为"$@"应该给出参数列表。但没有真正需要使用for arg in "$@"正确for arg的习语。

-o然后,为每个新参数添加一个,我们可以使用${findTarget[@]+"-o "}.
如果没有分配任何值,则它将为 null;如果分配了任何值,findTarget则它将是纯字符串。在第一次循环运行中,它将崩溃为空,在下一次循环中,它将变成。-ofindTarget-o

(( $# < 1 )) && echo "please provide some argument(s)" && exit 1

unset  findTarget
for    arg
do     [[ "$arg" == "$lastArg" ]] && break
       findTarget+=("${findTarget[@]+"-o "}-name $arg")
done

find . \( "${findTarget[@]}" \)

您需要记住按如下方式运行脚本:

$ trouver.bash '*.c' '*.f90' someString

如您所愿,每个参数都不会被 shell 扩展(使用单引号。双引号可能有效,但不能清楚地表达保留参数而不扩展的意图)。

如果您需要使用不带“单引号”的脚本,您可以删除“路径名扩展”(set -f)以避免扩展*.

$ ( set -f; trouver.bash *.c *.f90 someString )

()将避免对 noglob 的更改影响当前正在运行的 shell。

这是可行的,因为脚本会将结果写入stdout,它也可以从子 shell 内部进行打印(…)

相关内容