ESC
科技 1 分钟阅读

别把波浪号(~)放进你的 PATH

一篇技术博客指出常见陷阱:在 export PATH="$PATH:~/.local/bin/" 这类写法中,由于引号的存在,波浪号不会被展开为主目录,实际会生成一个字面的 ./~/ 目录,导致程序从错误位置被找到和执行。作者建议改用 $HOME,并提醒不加引号的写法在 Bash 和 Zsh 中虽可行但较脆弱,还提供了自查命令。

来源:Hacker News

export PATH="$PATH:~/.local/bin/"

不会将 ~(波浪号)展开为主目录路径(即 $HOME),因为波浪号到主目录的展开只会发生在未加引号的输入中,正如 bash 文档中所述:

如果一个单词以未加引号的波浪号字符(’~’)开头,那么直到第一个未加引号的斜杠(…)之前的所有字符都会被视为波浪号前缀。(…)

Bash 会检查每个变量赋值中紧跟在 ‘:’ 之后或第一个 ‘=’ 之后的未加引号波浪号前缀,并在这些情况下执行波浪号展开。(…)

因此,最终被添加到 PATH 中的不是 /home/<user>/.local/bin/,而是 ./~/.local/bin/。

export PATH="$PATH:$HOME/.local/bin/"

注意,不加引号的写法 export PATH=$PATH:~/.local/bin 在 Bash 和 Zsh 中实际上是可以工作的,因为变量赋值中在 ‘=’ 之后以及每个 ‘:’ 之后也会执行波浪号展开。但依赖这一点是脆弱的,例如,一个空格就会破坏变量赋值。

`$ ls -la total 0 drwxr-xr-x@ 2 dc staff 64 Oct 2 13:37 . drwxr-x—+ 105 dc staff 3360 Oct 2 13:37 .. $ mkdir -p .//.local/bin/ $ printf ‘#include <stdio.h>\nint main() { puts(“hello”); }’>a.c; gcc a.c -o .//.local/bin/kek $ PATH="/.local/bin/" kek hello $ tree -f . ├── ./ │ └── .//.local │ └── .//.local/bin │ └── ./~/.local/bin/kek └── ./a.c

4 directories, 2 files`

演示:PATH 中的字面波浪号会解析为当前工作目录下的 ./~/ 目录

如我们所见,kek 二进制文件是从 ./~/.local/bin/ 中找到并执行的——主目录根本没有参与其中。

你可以用以下命令快速检查自己是否存在这个问题:

$ echo "$PATH" | grep -- '~'

或者,将每个条目单独显示在一行: