少了一对引号,脚本就疯了

举报
yd_211440595 发表于 2026/11/05 11:35:30 2026/11/05
【摘要】 一个部署脚本本地测试好好的,换到带空格路径的正式环境当场报错。排查了路径、权限、编码、命令,最后发现是变量 $SRC_DIR 没加双引号,shell 把带空格的路径分词拆成了两半。一篇关于 shell 分词机制如何让脚本"精神分裂"的踩坑实录,附几条引号铁律。

那天写了个部署脚本,逻辑很简单:把构建产物从一个目录复制到另一个目录。源目录和目标目录都用变量存,方便复用:

本地测试,路径没空格,跑得好好的。换到正式环境一跑——

终端炸了。

cp 报了一串"找不到文件"的错误,指向一个叫 /home/user/my 的路径,和一个叫 project/build 的路径——把一个完整路径劈成了两半。

我当时第一反应:路径不存在?


我自信满满地开始排查。

  • 路径真的存在吗?ls /home/user/my\ project/build,存在,文件都在。
  • 是不是脚本里路径写错了?打开脚本看,/home/user/my project/build,一字不差,空格也在。
  • 是不是 cp 命令的问题?换成 rsync,还是炸。
  • 是不是变量赋值时空格被吃了?echo $SRC_DIR,输出 /home/user/my project/build,空格在,没问题。
  • 是不是脚本编码问题?查了,UTF-8,没 BOM,没问题。
  • 是不是这个路径里的空格是特殊空格(不间断空格、全角空格)?用 cat -A 看,就是普通空格。

到这里我已经开始怀疑人生了:变量里明明存的是完整路径,echo 出来也对,为什么 cp 一用就劈成两半?

一个上午就在这种"变量明明是对的、命令明明是对的、但凑一起就炸"的诡异里过去了。中间我还把路径改了名、去掉了空格——果然不炸了。但那是正式环境的目录结构,不能随便改名。


中午,我盯着那行 cp -r $SRC_DIR/* $DEST_DIR/ 发呆,突然反应过来——

问题不在变量,也不在命令,在我没给变量加引号。

shell 在解析命令时,会对未加引号的变量做两件事:分词(word splitting)和通配符展开(globbing)。

所谓分词,就是 shell 把变量值按空格、制表符、换行符拆成多个独立的"词"。我的 $SRC_DIR 值是 /home/user/my project/build,因为没加引号,shell 把它拆成了两个词:/home/user/my 和 project/build。

于是那行命令在 shell 眼里变成了:

——把 my 当成源、把 project/build/* 当成另一个源,然后复制到目标。两个源都不存在,自然报错。

解决简单到令人窒息:

一对双引号,告诉 shell:"这个变量是一个整体,别给我拆。"

跑完,乖乖工作。

折腾一上午换来的几句话,送给每一个"用 shell 变量不加引号"的人:

  1. shell 里用变量,永远加双引号。 这是最便宜、收益最高的一条习惯。"$VAR" 比 $VAR 安全一万倍——双引号会保留变量的完整值,阻止分词和通配符展开。除了你明确想让它分词的场景(极少),一律加引号。
  1. 路径带空格是引号问题的头号触发场景。 没空格时变量不加分词也看不出来,一旦路径带空格、带特殊字符($、*、?、~),不加引号必炸。Windows 挂载目录、用户主目录带空格(My Documents)、中文路径,全是雷区。
  1. echo $VAR 看不出问题,要 echo "$VAR"。 不加引号的 echo 会先分词再合回来,肉眼看像是完整的,其实变量在被使用时已经被拆过。诊断变量值,永远加引号 echo,或者用 printf。
  1. 用 shellcheck 给脚本体检。 shellcheck 是 shell 脚本的 linter,能自动揪出"变量未加引号"这类问题,附带解释为什么该加。写完脚本过一遍 shellcheck,能挡掉九成的引号坑。这是花十秒钟省两小时的典型操作。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。