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

Gnu*_*iff 1 bash find shell-script arguments

我需要使用“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}`
Run Code Online (Sandbox Code Playgroud)

这有效。

但是,只要我想使用变量搜索返回的天数,就像这样:

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}`
Run Code Online (Sandbox Code Playgroud)

find找不到任何匹配的文件时,该函数失败,并出现以下错误:

未找到命令 '-name',您的意思是:命令 'uname' 来自包 'coreutils' (main) -name: command not found

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

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

Kus*_*nda 5

在 中bash,使用数组:

args=( '(' -name 'FOO*.xml' -or -name 'BAR*.xml' -or -name 'BTT*.txt' ')' )
Run Code Online (Sandbox Code Playgroud)

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

然后,在find命令中:

find ...some arguments... "${args[@]}"
Run Code Online (Sandbox Code Playgroud)

你有一个额外的问题,你使用

`$ARGLINE`
Run Code Online (Sandbox Code Playgroud)

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


ilk*_*chu 5

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

$ foo='foo "bar doo"'
$ printf "<%s>\n" $foo
<foo>
<"bar>
<doo">
Run Code Online (Sandbox Code Playgroud)

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

ARGS=(-name 'FOO*.xml' -or -name 'BAR*.xml' -or -name 'BTT*.txt')
Run Code Online (Sandbox Code Playgroud)

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

find . "${ARGLINE[@]}"
Run Code Online (Sandbox Code Playgroud)

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

ARGS=(-name 'FOO*.xml')
ARGS+=(-or -name 'BAR*.xml')
ARGS+=(-or -name 'BTT*.txt')
Run Code Online (Sandbox Code Playgroud)

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


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

set -f       # disable globbing
ARGLINE="-name FOO*.xml -or -name BAR*.xml -or -name BTT*.txt"
find . $ARGLINE
Run Code Online (Sandbox Code Playgroud)