shell - 为什么 shell 对我的“查找”命令的内联解释与执行的脚本不同?
问题描述
我正在编写一个用 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”命令。不知何故,“模板”中的内容可以完成,而“其他内容”中的内容却没有。即使目录存在,“查找”命令似乎也没有返回任何结果。
奇怪的是,当我打开我的 shell,内联声明变量,并执行与上面循环段中相同的“查找”时,我得到了结果。我尝试从“key”周围删除单引号并将它们放在关联数组键定义周围,如下所示:
DIRSET["'Other Content'"]='/templates'
...
find . -type d -name "$key"
但这也不会从“查找”中产生任何结果。我将命令放在脚本中的 echo 中,以确保变量按预期传递到命令中:
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 尝试将我的字符串解释为一系列以空格分隔的命令时出现错误。
有什么理由为什么“查找”命令会以一种内联方式工作但在执行的脚本中以不同的方式工作?感觉就像我正在寻找就在我鼻子前面的东西。一些外部观点将不胜感激。
注意:源系统和目标系统都有 Bash 4 版本(分别为 4.4 和 4.3),因此应该允许关联数组。
解决方案
我认为根本的误解是在 shell 命令字符串和程序调用之间。
像这样的 shell 命令字符串:
find . -type d -name 'Other Content'
将变成一个参数列表并find
像这样传递给:
find
, .
, -type
, d
, -name
,Other 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
直接运行您的命令。
推荐阅读
- python - Python子进程以异步方式Popen
- javascript - 尽管值已定义,Node.js 函数仍返回未定义
- python - 如何从python列表中的最小值开始?
- sql - 使用 SELECT 将数据插入按表 PL/SQL 的索引
- xslt - 你可以在 XSLT 中替换一个 unicode 字符吗?
- java - 如何使用按钮更改 Textview 的值以遍历 ArrayList?
- python - Microsoft Graph - Create Teams API 返回“包含导航属性不支持的绑定请求”。
- jquery - JQuery:收到此错误未捕获的类型错误:无法读取未定义的属性“scrollHeight”
- matlab - Matlab数字滞后
- html - 带有滚动的 ui 内 100% 宽度