在 bash 中构建外部命令的可变长度参数行的正确方法

在 bash 中构建外部命令的可变长度参数行的正确方法

我需要使用“find”命令在 Bash 函数中查找几组不同的文件,具体取决于我的脚本输入。

所以,我有这样的事情:

DAYS=30
case $1 in
A1) ARGLINE="-name 'FOO*.xml' -or -name 'BAR*.xml' -or -name 'BTT*.txt'"
    ;;
A2) ARGLINE="-name 'PO*xml' -or -name 'PR*xml'"
    ;;
...
esac
find . -maxdepth 1 -type f -mtime +${DAYS} `${ARGLINE}`

这有效。

但是,一旦我想使用变量来搜索天数,如下所示:

DAYS=30
case $1 in
A1) ARGLINE="-name 'FOO*.xml' -or -name 'BAR*.xml' -or -name 'BTT*.txt'"
    ;;
A2) ARGLINE="-name 'PO*xml' -or -name 'PR*xml'"
    ;;
...
esac
if [[ $# -gt 1 ]]; then
    DAYS=$2
fi
find . -maxdepth 1 -type f -mtime +${DAYS} `${ARGLINE}`

该功能失败时寻找没有找到任何匹配的文件,出现以下错误:

找不到命令“-name”,您的意思是:来自包“coreutils”(主)的命令“uname”-name:找不到命令

然而,当 find 找到一些文件的天数达到这个值时,它就能正常工作。当我尝试将成功运行的输出通过管道传输到另一个命令时,它也会失败。

我应该如何正确构建“查找”的论证行?

答案1

在 中bash,使用数组:

args=( '(' -name 'FOO*.xml' -or -name 'BAR*.xml' -or -name 'BTT*.txt' ')' )

由于您使用 ),因此额外的括号用于创建正确的布尔逻辑分组-or

然后,在find命令中:

find ...some arguments... "${args[@]}"

您还有一个额外的问题,因为您使用

`$ARGLINE`

这是一个命令替换,类似于$( $ARGLINE )shell 将尝试将$ARGLINE(其值)作为命令执行。这就是为什么您会收到“找不到命令‘-name’”。命令替换失败,但find运行,这就是您认为它“有效”的原因。

答案2

这里的主要问题是引号在引号内不起作用:扩展变量后,其中的引号只是普通字符,例如:

$ foo='foo "bar doo"'
$ printf "<%s>\n" $foo
<foo>
<"bar>
<doo">

您应该使用数组来存储命令参数,如下所示:

ARGS=(-name 'FOO*.xml' -or -name 'BAR*.xml' -or -name 'BTT*.txt')

shell 将在此阶段处理引用,并将不同的 shell 单词存储为不同的数组元素。像这样使用数组:

find . "${ARGLINE[@]}"

如果您愿意或需要,您可以逐段构建数组,这应该会产生相同的数组:

ARGS=(-name 'FOO*.xml')
ARGS+=(-or -name 'BAR*.xml')
ARGS+=(-or -name 'BTT*.txt')

但是,请注意,您还使用了反引号来“引用” ${ARGLINE}。这将启动命令替换并将 的内容ARGLINE作为命令运行。这就是你的错误的来源,shell 尝试运行一个名为 的程序-name


事实上,在你的例子中,你并没有需要一个数组,因为参数中没有任何空格。这里的主要问题通常是空白之间的区别之间参数和空格之内当命令行存储为字符串时,参数会丢失。但就你而言,这可能有效,但我不推荐它:

set -f       # disable globbing
ARGLINE="-name FOO*.xml -or -name BAR*.xml -or -name BTT*.txt"
find . $ARGLINE

相关内容