使用zsh
(和oh-my-zsh
),是否可以始终在某个其他命令之后运行某个命令?我希望始终在进入目录ls
后运行某个命令cd
,而不是cd example; ls
每次都运行。
我努力了:
function cd {
cd $1
ls
}
但它似乎zsh
在运行时退出,没有任何输出(堆栈溢出?)。更改函数名称使其按预期工作,但我宁愿仍然使用该cd
命令。
alias cd='cd $1;ls'
列出指定的目录,但实际上并不改变工作目录。
可以这样做吗?如果可以,怎么做?
答案1
尽管您自己已经解决了您的问题,但我还是想详细说明一下。
为什么你的别名不起作用:
@evilsoup 已经暗示了这一点,但要明确说明:别名(在嘚和狂欢)不能接受参数(而在韓軟體他们可以)。 将其视为一个简单的字符串替换。 因此,使用alias cd='cd $1;ls'
defined,命令cd foo
扩展为,cd ; ls foo
因为$1
是空的(在此上下文中不是定义),并且您会看到的内容,foo/
但 shell 不会cd
进入当前的目录(后面没有参数cd
)。
为什么问题中的功能不起作用:
你定义了一个函数cd
,它本身调用cd
。因此你最终陷入了无限循环。当前的 zsh 版本 (5.0.2) 不会出现段错误,但会出现错误:
cd:1: maximum nested function level reached
builtin cd
解决方案是在您的函数内部使用cd
——正如您自己已经解决的那样。
为什么你的功能(来自那里)可以改进
您正在重新定义该cd
函数。如果您明确调用它,效果很好。但 zsh 提供了AUTO_CD
选项(使用 进行设置setopt AUTO_CD
):
如果发出的命令不能作为普通命令执行,并且该命令是目录名称,则执行 cd 命令到该目录。
/var
这意味着您只需输入/var
而不是即可更改为cd /var
。在这种情况下,您的cd
函数不会被调用,并且您不会自动看到 的内容/var
。
pushd
和popd
还有一些情况,重新定义的cd
命令没有帮助。
ZSH 方式
Stephane Chazelas 在 SE/UL 上已经给出了 zsh-ish 的方式,正如 @evilsoup 在评论中指出的那样:不要重新定义cd
,而是使用钩子函数chpwd()
,它的存在正是出于这个原因:
chpwd
每当当前工作目录改变时执行。
因此只需添加
function chpwd() {
ls
}
到你的~/.zshrc
。 (其简短形式chpwd() ls
如链接答案中所示。)
答案2
答案3
不要chpwd()
直接定义函数。
在 ZSH 中,有一个钩子chpwd
专门用于在目录改变后运行命令。
不要chpwd()
直接定义函数。它将覆盖挂载在 上的其他函数chpwd
。典范方法是定义一个函数,并将其加入到hook中chpwd
,使得该函数与hook中的其他函数共存chpwd
。
# Hook onto `chpwd` in the right way
# with the official util add-zsh-hook
# wrap command `ls` into a function
_ls_on_cwd_change() {
command ls;
}
# load add-zsh-hook if it's not available yet
(( $+functions[add-zsh-hook] )) || autoload -Uz add-zsh-hook
# hook _ls_on_cwd_change onto `chpwd`
add-zsh-hook chpwd _ls_on_cwd_change
# Additional info
# list existing hook functions
add-zsh-hook -L
# or
add-zsh-hook -L chpwd
答案4
我知道这已经有 10 年了,但我认为我只需发布一个当前时间的答案,这样到达这里的人可能会受益:
最简单的方法通常是最好的方法。过于复杂的启动脚本会导致以下问题:实际上需要添加一些复杂的东西。为此,我会使用别名与匿名函数:
alias cd='() { builtin cd "$@"; ls; }'
如果你已经有了 ls 的别名,不希望在上面的别名中使用,您只需稍微修改一下就可以避免这种情况:
alias cd='() { builtin cd "$@"; command ls; }'
这是一种简单的方法,如果您需要在任何时间点直接运行真正的 cd 命令,您只需使用:
builtin cd <args>
如上一个别名所示。由于您处于匿名函数中,因此您可以做得更花哨。您可以在参数中添加分隔符,如“==”,然后将它之前的所有内容发送到光盘以及之后的一切ls. 最简单的形式(但远非完善):
alias cd='() { eval builtin cd ${${(s/==/)*}[1]}; eval ls ${${(s/==/)*}[2]}; }
(/s//)
此处使用扩展标志来拆分为$*
2 个数组。我们$*
在此处使用是因为它是数组的标量形式$@
。然后使用来eval
强制解释器按原样(重新)评估输入行,将命令拆分为输入时的样子。
优点是您可以选择为 ls 提供过滤器或搜索选项,同时仍然能够在单个命令行中将常规命令行传递给 cd。
免责声明:
缺点是,像这样的字符串==
很特殊,除非你用引号括起来,否则 zsh 会尝试扩展它"=="
。因此,你可能希望使用其他标记对(尽管它没有有成对出现)。我在这里使用它只是为了举一个简单的例子。
另外,请注意,如果您使用通配符,则必须将其括在引号中。这是因为通配符发生在执行命令之前。例如,如果您在目录 /home/myhome 中,并且您执行了以下操作:
cd /tmp "==" tmp*
您将获得与执行以下操作相同的结果:
builtin cd /tmp
command ls /home/myhome/tmp*
相反,你必须这样做:
cd /tmp "==" "tmp*"
这样,在处理命令行时eval
就会处理 glob 。ls
此外,还有其他更有效的方法来分隔数组,但我认为这个示例即使是初学 zsh 用户也应该能够理解,而不必记住整个 zsh 手册。最后一个例子只是一个例子,还有很多更好的方法可以做到这一点。
无论如何,这对于 necro 助手帖子来说可能有点过分。我只是碰巧看到它并认为它很奇怪,因为即使在那时 zsh 中也存在匿名函数。希望它能对某些人有所帮助,这就是我发布它的原因。
:3