背景:我已经有了一个工作别名my-tool
,如下所示:
alias my-tool='~/path/to/src/my-tool.py'
我想要另一个依赖于该别名路径的别名(这样我就不会在两个地方写路径(:
alias other-tool=$'$(dirname $(dirname $(which my-tool | awk '{ print($NF) }')))/script/other-tool.sh'
其输出错误
zsh: no such file or directory: ~/path/to/script/other-tool.sh
但它是存在的!
奇怪的是,如果我用替换别名
alias other-tool=$'$(dirname $(dirname ~/path/to/src/my-tool.py))/script/other-tool.sh'
它是有效的,但我还是想避免两次进入~/path/to/..
很明显,awk
、dirname
或which
中都存在意外行为,有人能解释为什么会出现错误吗?
别名的开头有一个波浪号字符。当别名作为命令被调用时,这是有效的,因为别名扩展的结果会进行单词扩展。但随后您尝试对别名的文本进行一些处理,这会产生一个要用作路径的字符串。该字符串仍然包含波浪号字符,因此您尝试使用名为~
的目录。
如果您真的想使用my-tool
别名作为定义other-tool
别名的基础,请在定义别名时进行字符串处理。不要使用type
或which
:它们是显示附加消息的最终用户命令。使用aliases
关联数组直接获取别名的文本。使用zsh的内置构造来操作字符串(参数扩展(或路径(历史修饰符(:它们比使用外部工具更容易正确。
alias other-tool=$aliases[my-tool]:h:h/scripts/other-tool.sh
或
alias other-tool=${aliases[my-tool]%/*/*}/scripts/other-tool.sh
但是,用根路径定义一个变量,并在两个别名中使用该变量,在概念上更简单,也更可靠。
tool_root=~/path/to
alias my-tool='$tool_root/src/my-tool.py'
alias other-tool='$tool_root/scripts/other-tool.py'
肯定有更好的方法可以做到这一点(我喜欢@Luuk在评论中提到的路径前缀变量(,但与您尝试的方法等效的工作方法是:
alias other-tool=$'loc=($(type my-tool)); ${~loc[-1]:h:h}/script/other-tool.sh'
这不是每次运行别名时都运行awk
和几个dirname
进程,而是使用zsh
参数子例程集成-首先,它将loc
数组变量设置为运行type my-tool
的结果,每个单词都有自己的元素,然后对于最后一个元素(路径(::h
修饰符的作用类似于dirname
,执行两次,然后~
打开该扩展的GLOB_SUBST
选项,该选项执行文件名扩展,捕获~
。
我处理波浪号(~
(的方法只是将其展开一次,而不是用单引号('
(硬引用它们-这样我就不必手动用"${HOME}"
:替换它
testfn="$( grealpath -ePq ~/.zshenv )" # g- for gnu
echo "${testfn}"
ls -AlF "${testfn}"
/Users/user123/.bash_profile
-rw-r--r-- 1 user123 staff 1746187 Aug 27 23:54 /Users/user123/.bash_profile
这实际上是正确的输出,因为我的zsh
环境文件实际上是一个符号链接:
lr--r--r-- 1 user123 staff 15 Dec 31 2019 /Users/user123/.zshenv -> ./.bash_profile