我在使用 FreeBSD 时偶然发现了 zsh 的这种行为:
% dd if=/dev/zero bs=1M count=1 of=~/test2
dd: failed to open '~/test2': No such file or directory
这真的让我很困惑,因为同样的事情在 bash 中工作得很好。
我可以touch
在 zsh 中使用波形符来文件,然后ls
它们:
% touch ~/test2
% ls ~/test2
/home/christoph/test2
起初,我假设 zsh 没有意识到后面有一条路径,of=
所以它没有扩展~
。但自动完成文件名效果很好。事实上,如果使用现有文件名,以 开头其路径~
,然后在某个时刻按 Tab 键,该路径会在我输入的命令中展开。
为什么 zsh 传递~/test2
给dd
,而不是/home/christoph/test2
?
zsh 在 Linux 上的行为相同。事实上,我执行了上面的这些命令并在 Linux 机器上复制了它们的输出。
答案1
~
仅在少数情况下扩展。 POSIX,用于标准sh
要求echo a=~
输出a=~
(同时要求单独~
扩展a=~
)。
zsh
但是有一个magicequalsubst
选项,您可以使用该选项~
在 ¹ 之后进行扩展,=
即使它不在export
/ typeset
... 伪关键字的赋值或参数中。
所以:
$ echo a=~
a=~
$ set -o magicequalsubst
$ echo a=~
a=/home/chazelas
请注意bash
,当不在 POSIX/sh
模式下时,仅当 左侧的内容看起来像字面的不带引号的变量名时才展开~
in (无论它是否在 // 或任何其他命令的参数中):word=~
=
bash
typeset
declare
export
$ bash -c 'echo a=~'
a=/home/chazelas
$ bash -c 'echo "a"=~'
a=~
$ bash -c 'var=a; echo $var=~'
a=~
$ bash -c 'echo a.b=~'
a.b=~
$ (exec -a sh bash -c 'echo a=~')
a=~
在任何情况下,您始终可以使用$HOME
(~
在echo a=$HOME
zsh 中,echo "a=$HOME"
在其他类似 Bourne 的 shell 中,其中需要引用参数扩展以防止 split+glob²)。
仅限文字和未引用的内容,或由globsubst
启用的扩展产生的内容,其中文件名扩展(从.....分离通配又名文件名生成)~
是 的一种形式,只要shfileexpansion
未启用(如在sh
仿真中),就会执行。你会发现这var='a=~' zsh -o magicequalsubst -o globsubst -c 'echo $var'
两个var='a=~' zsh -o magicequalsubst -c 'echo $~var'
输出a=/home/dir
。
² 波形符扩展本身不会经历 split+glob,除非在旧版本的 bash 中它曾经经历过 globbing。无论如何,波浪线扩展不会在双引号内执行。