Shell编写计算器时如何保留$变量解析并禁止*通配符展开
问题本质
你遇到的*通配符展开和脚本内部逻辑无关,是调用脚本的父Shell在启动脚本前就完成的替换:你敲完回车、脚本还没开始运行的时候,交互Shell就已经把未转义的*替换成当前目录下的所有文件名了。你在脚本内部给变量加引号根本拦不住这个阶段的展开,因为参数传到脚本里时,$2早就不是*了。
你之前测试引号的误区:
- 双引号不是没用,是你加错了位置:双引号确实能阻止glob展开,但必须加在调用脚本的命令行里,加在脚本内部的变量上没有任何作用
- 单引号会输出
$1$2$3=字面量是bash的基础规则:单引号内部所有字符都按字面量处理,不会解析变量、执行转义
解决方案
规范调用方式(最稳妥无兼容问题)
只要在调用时把*转义,让父Shell不要展开它,你原来写的脚本不用改就能正常运行,三种合法转义写法任选其一即可:
- 给
*加单引号:calc 2 '*' 3 - 给
*加双引号:calc 2 "*" 3 - 用反斜杠转义:
calc 2 \* 3
执行后都会正确输出2*3=6。
脚本兼容优化(用户无需转义也能运行)
如果不想让用户记转义规则,可以在脚本开头加参数校验逻辑,自动识别*被展开的场景,修正参数后再运算。优化后的完整脚本如下,顺便替换了效率更低的外部expr命令为bash原生算术扩展,补了除零判断,修正了拼写错误:
#!/bin/bash -u PATH=/bin:/usr/bin ; export PATH umask 022 num1=$1 op=$2 num2=$3 # 检测参数数量异常,判断是否为*被glob展开的场景 if [ $# -ne 3 ]; then # 校验首尾参数是否为整数,符合calc 数字 * 数字的调用特征 if [[ $1 =~ ^-?[0-9]+$ && ${!#} =~ ^-?[0-9]+$ ]]; then op='*' num2=${!#} else echo "调用格式错误,正确用法:calc <数字> <运算符(+ - * /)> <数字>" exit 1 fi fi echo -n "$num1$op$num2=" case "$op" in '+' ) echo $((num1 + num2)) ;; '-' ) echo $((num1 - num2)) ;; '*' ) echo $((num1 * num2)) ;; '/' ) if [ "$num2" -eq 0 ]; then echo "错误:除数不能为0" exit 1 fi echo $((num1 / num2)) ;; * ) echo 'unknown operation' ;; esac
这个兼容逻辑的原理是:如果用户输入calc 2 * 3时*被展开,传入脚本的参数会变成2 文件名1 文件名2 ... 3,此时参数数量肯定大于3,只要第一个和最后一个参数是合法整数,就可以判定中间的内容是被展开的*,直接取首尾参数作为运算数、运算符固定为*即可得到正确结果。
内容的提问来源于stack exchange,提问作者Ömer
相关产品推荐
相关产品推荐

