为什么Shell内联地解释我的“查找”命令与执行脚本的解释不同?

时间:2019-04-11 21:44:21

标签: shell sh

我正在研究用Shell编写的部署脚本,该脚本将在Linux服务器上执行。部分要求是,源端的某些文件夹会聚合到目标端的单个文件夹中。这些不会随着项目的历史而改变,因此我为它们创建了一个关联数组,如下所示:

declare -A DIRSET
DIRSET["Content_A"]='/contents'
DIRSET["Content_B"]='/contents'
DIRSET["Templates"]='/templates'
DIRSET["Other Content"]='/templates'

此关联数组用于将源目录映射到目标目录。稍后,在我的脚本中,我将使用类似于以下的循环遍历这些内容:

for key in "${!DIRSET[@]}"
do
    SUBDIR=${DIRSET[$key]}
    find . -type d -name "'$key'" | while read line; do
        if ls -1qA "${line}/" | grep -q
        then
        # to strip away './'
        OLDDIR=${line:2:${#line}}
        SUBPATH="${PROJ_ROOT}/AggregateFolder/${SUBDIR}"
        rsync -rvic -e "ssh some/.ssh/dir" "${OLDDIR}/" "${SUBPATH}"
        fi
done

此循环使用“ find”查找要聚合的目录,然后将其输入“ rsync”命令。 “模板”中的内容以某种方式使它变得完整,而“其他内容”中的内容则没有。即使该目录存在,“ find”命令似乎也不会返回任何结果。

奇怪的是,当我打开外壳,内联声明变量并执行与上述循环段相同的“查找”时,我得到了结果。我试图从“键”周围删除单引号,并将它们放在关联数组键定义周围,如下所示:

DIRSET["'Other Content'"]='/templates'
...
find . -type d -name "$key"

但是,这也不会产生“查找”结果。我将命令放在脚本的回显中,以确保变量按预期方式传递到命令中:

echo "find . -type d -name $key"
find . -type d -name "$key"

但是输出最终是这样的,其中循环中的所有find命令都不起作用:

...
find . -type d -name Templates
find . -type d -name 'Other Content'

请注意,如果我在关联数组键或其“ $ key”引用周围没有单引号,则在shell尝试将我的字符串解释为一系列以空格分隔的命令时,会出现错误。

是否有任何原因可以使“ find”命令以一种内联方式运行,但在执行的脚本中以不同方式运行?感觉就像我在寻找鼻子前的东西。一些外部的观点将不胜感激。

注意:源系统和目标系统都具有Bash 4版本(分别为4.4和4.3),因此应允许使用关联数组。

1 个答案:

答案 0 :(得分:1)

我认为根本的误解是在shell命令字符串和程序调用之间。

这样的shell命令字符串:

find . -type d -name 'Other Content'

将变成一个参数列表,并传递给find,如下所示:

find.-typed-nameOther Content

由这个参数列表决定find的作用。这就是真理的源头。任何导致该参数列表的命令字符串都将以您想要的方式运行find。任何导致不同参数列表的命令字符串都将执行其他操作。因此,它正在构建您应该努力的参数列表。

试图获取echo以打印shell命令字符串没有什么价值,就像您不会通过显示console.log(..)print(..)来尝试编写JS或Python一样您要运行的语句。

要更准确地了解结果参数,可以使用printf

$ printf 'Argument: <%s>\n' find . -type d -name 'Other Content'
Argument: <find>
Argument: <.>
Argument: <-type>
Argument: <d>
Argument: <-name>
Argument: <Other Content>

如果要从变量的内容中获得相同的结果,则应确保命令输出相同的内容。就您而言,不是:

$ printf 'Argument: <%s>\n' find . -type d -name "'$key'"
Argument: <find>
Argument: <.>
Argument: <-type>
Argument: <d>
Argument: <-name>
Argument: <'Other Content'>  # Different argument, so different result

同样的技术显然也可以证明您的其他尝试是如何失败的:

$ key="'Other Content'"
$ printf 'Argument: <%s>\n' ... -name $key
Argument: <...>
Argument: <-name>
Argument: <'Other>      # Both extra apostrophes
Argument: <Content'>    # and bad splitting

当两个printf语句生成相同的参数列表时(仅在您的情况下使用"$key"),就可以删除printf '..'位并运行find命令直接。