简短回答:请参阅BashFAQ #50:我正在尝试将命令放入变量中,但复杂的情况总是失败!.
长答案:当 shell 解析类似的命令时find / ${CRITERIA}
,它首先解析引号、转义等;然后它${CRITERIA}
用它们的值替换变量,但不会返回通过它们来查找引号等——它已经完成了。结果,将引号、转义符等放在变量中并没有任何用处。
在 bash 中做这种事情的最好方法是将参数存储在一个数组中:
#!/bin/bash
CRITERIA=(-name "*.[ch]" -o -name "*.cpp" -o -name "*.hpp")
echo "Find command: find / ${CRITERIA} > /tmp/output.txt"
find / "${CRITERIA[@]}" > /tmp/output.txt
...但这不适用于您将 $1 视为一组可选标准的方式。脚本是否需要任何其他参数?如果没有,我建议将搜索条件作为单独的参数而不是单个参数(即./findscript -name "*.c"
代替./findscript "-name \"*.c\""
),如下所示:
#!/bin/bash
if [ $# -gt 0 ]; then
CRITERIA=("$@")
else
CRITERIA=(-name "*.[ch]" -o -name "*.cpp" -o -name "*.hpp")
fi
echo "Find command: find / ${CRITERIA} > /tmp/output.txt"
find / "${CRITERIA[@]}" > /tmp/output.txt
编辑:根据您关于需要允许脚本其他选项/参数的评论,我看到三个选项:
用于eval
将 getopt 参数解析为数组:
case "$opt" in
f) eval "CRITERIA=($OPTARG)" ;;
这具有与 相关的所有常见问题eval
,主要与以奇怪(并且可能是危险的)方式失败有关,具体取决于作为 OPTARG 传递的内容。伊克。
如果脚本不需要采用任何“常规”(非选项)参数,则将条件作为常规参数 ( ./script -a -b -c -d -- -name ".cpp" -o -name ".hpp"
) 传递。这样做的问题是 IIRCgetopts
并不--
作为选项和常规参数之间的分隔符,因此您必须至少“手动”进行一些解析。更正:至少在 bash 版本 3.2.48 中,自动getopts
处理--
。
如果您需要传递的唯一标准是-name,您可以使用多个-f
选项来指定名称模式来构建标准,例如./script -a -b -c -d -f "*.cpp" -f "*.hpp"
CRITERIA=()
...
case "$opt" in
f) if [[ ${#CRITERIA[@]} -eq 0 ]]; then
CRITERIA=(-name "$OPTARG")
else
CRITERIA+=(-o -name "$OPTARG")
fi ;;
...
if [[ ${#CRITERIA[@]} -eq 0 ]]; then
CRITERIA=(-name "*.[ch]" -o -name "*.cpp" -o -name "*.hpp")
fi