echo "$HOME"保留空格,但允许 $变量 和 $(命令) 展开。日常引用变量首选。
约 1301 字大约 4 分钟
2026-05-14
新手在命令行踩的坑,一大半和"Shell 在你按回车之前,先把这行字改了一遍"有关。变量被展开、* 被替换成一堆文件名、空格把一个参数拆成两个——这些都发生在命令真正执行之前。引号和转义,就是你控制这层"预处理"的工具。
理解 Shell 在执行命令前做了哪些展开,用单引号、双引号、反斜杠精确控制参数,避免被空格和通配符坑到。
echo "$HOME"保留空格,但允许 $变量 和 $(命令) 展开。日常引用变量首选。
echo '$HOME'完全原样,里面的 $ 不展开。要输出字面量时用。
ls *.log* 由 Shell 先展开成匹配的文件名,再交给 ls。
echo a\ b转义单个字符,这里让空格不被当作参数分隔。
输入 cp $file *.bak ~/backup 后,Shell 不会原样把这些字交给 cp,而是先做几轮处理:
$file 替换成它的值*.bak 替换成当前目录所有匹配的文件名~ 替换成家目录路径cp 最终收到的,是处理完的结果。引号的作用,就是关掉其中某几轮处理。
| 写法 | 变量 $ 展开 | 通配符 * 展开 | 空格保留 |
|---|---|---|---|
| 不加引号 | 是 | 是 | 否,会被拆开 |
双引号 "..." | 是 | 否 | 是 |
单引号 '...' | 否 | 否 | 是 |
记忆口诀:单引号是"原样照搬",双引号是"允许变量、保留空格",不加引号是"全都展开"。
name="my file"
echo $name # my file —— 被拆成两个参数
echo "$name" # my file —— 一个完整参数
echo '$name' # $name —— 完全字面量这是 Shell 最重要的一条习惯。变量的值里只要含空格,不加引号就会被拆开:
file="report 2024.txt"
rm $file # 危险!变成 rm report 2024.txt —— 删了两个错的目标
rm "$file" # 正确,删的是 "report 2024.txt" 这一个文件凡是 "$var"、"$(cmd)"、"${arr[@]}",默认都加双引号。只有极少数确实需要单词拆分的场景才故意不加。
*、?、[abc] 这些通配符(glob),由 Shell 在命令执行前展开成文件名列表,命令本身根本不知道你写过 *:
ls *.log # Shell 先变成 ls a.log b.log c.log,再执行由此产生两个常见困惑:
*.log 原样传过去,于是你会看到 ls: cannot access '*.log'。find -name "*.log"),反而要加引号阻止 Shell 展开,把 *.log 原样交给 find。find . -name "*.log" # 对:引号让 find 收到字面量 *.log
find . -name *.log # 错:Shell 先展开,find 收到的是已匹配的文件名批量删除前,先把 rm 换成 ls
rm *.tmp、rm log-* 这类命令,危险的不是 rm,是你对 * 实际匹配到什么的猜测。养成习惯:先用同样的通配符跑一次 ls,确认匹配列表正是你想删的,再把 ls 改成 rm。尤其警惕 rm * .tmp(多打一个空格)——它会先删光当前目录所有文件。
| 误区 | 更稳妥的做法 |
|---|---|
| 用变量时不加引号 | 一律 "$var";含空格的值不加引号会被拆成多个参数 |
以为 find -name *.log 里的 * 是 find 处理的 | 不加引号会被 Shell 先展开;要给 find 的通配符加引号 |
rm 直接配通配符,凭感觉 | 先 ls 同样的通配符确认范围,再改成 rm |
| 单引号里想用变量 | 单引号不展开变量;需要变量就用双引号 |
版权归属:Shuo Liu