首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
skywind3000
V2EX  ›  Linux

更懂你的路径切换工具 - z.lua(集 autojump / z / fasd 大成)

  •  4
     
  •   skywind3000 · 352 天前 · 4831 次点击
    这是一个创建于 352 天前的主题,其中的信息可能已经有所发展或是发生改变。

    z.lua 是一个快速路径切换工具,它会跟踪你在 shell 下访问过的路径,通过一套称为 Frecent 的机制(源自 FireFox ),经过一段简短的学习之后,z.lua 会帮你跳转到所有匹配正则关键字的路径里 Frecent 值最高的那条路径去。

    正则将按顺序进行匹配,"z foo bar" 可以匹配到 /foo/bar,但是不能匹配 /bar/foo。

    项目地址:skywind3000/z.lua

    有啥特点?

    • 性能比 z.sh 快三倍,比 fasd / autojump 快十倍以上。
    • 支持 Posix Shell ( bash, zsh, dash, sh, ash, busybox )及 Fish Shell。
    • 支持 Windows cmd 终端 (使用 clink),cmder 和 ConEmu。
    • 无依赖,不会像 fasd/z.sh 那样对 awk/gawk 有特殊的版本要求。
    • 兼容 lua 5.1, 5.2 和 5.3 以上版本。
    • 新增:环境变量 "$_ZL_ADD_ONCE" 设成 1 的话性仅当前路径改变时才更新数据库。
    • 新增:增强匹配模式,将环境变量 "$_ZL_MATCH_MODE" 设置成 1 可以启用。
    • 新增:交互选择模式,如果有多个匹配结果的话,跳转前允许你进行选择。

    如何安装?

    Posix Shells ( Bash、zsh、dash、sh 或 BusyBox 等)中,在你的 .bashrc, .zshrc 或者 .profile 文件中按 shell 类型添加对应语句:

      eval "$(lua /path/to/z.lua  --init bash)"   # BASH 初始化
      eval "$(lua /path/to/z.lua  --init zsh)"    # ZSH 初始化
      eval "$(lua /path/to/z.lua  --init posix)"  # Posix shell 初始化
    

    用下面参数初始化会进入“增强匹配模式”:

      eval "$(lua /path/to/z.lua  --init bash once enhanced)"   # BASH 初始化
      eval "$(lua /path/to/z.lua  --init zsh once enhanced)"    # ZSH 初始化
      eval "$(lua /path/to/z.lua  --init posix once enhanced)"  # Posix shell 初始化
    

    同时 zsh 支持 antigen/oh-my-zsh 等包管理器,可以用下面路径:

      skywind3000/z.lua
    

    进行安装,比如 antigen 的话,在 .zshrc 中加入:

      antigen bundle skywind3000/z.lua
    

    就可以了(主要要放在 antigen apply 语句之前)。Windows 和 Fish Shell 的初始化见文档。

    Matching

    z.lua 提供两种路径匹配算法:

    • 设置 $_ZL_MATCH_MODE=0:默认匹配算法,兼容 z.sh
    • 设置 $_ZL_MATCH_MODE=1:增强匹配算法,更懂你的高效匹配算法。

    除了设置环境变量外,还可以通过:

    eval "$(lua /path/to/z.lua --init bash enhanced)"
    

    来进入增强模式。

    默认匹配

    默认情况下 z.lua 使用和 z.sh 类似的匹配算法,成为默认匹配法。给定路径会按顺序匹配各个正则表达式。

    • cd 到一个包含 foo 的路径:

      z foo
      
    • cd 到一个以 foo 结尾的路径:

      z foo$
      
    • 使用多个参数进行跳转:

      假设路径历史数据库(~/.zlua )中有两条记录:

      10   /home/user/work/inbox
      30   /home/user/mail/inbox
      

      "z in"将会跳转到 /home/user/mail/inbox 因为它有更高的权重,同时你可以传递更多参数给 z.lua 来更加精确的指明,如 "z w in" 则会让你跳到 /home/user/work/inbox

    增强匹配

    你可以通过设置环境变量来启用增强匹配模式:

    export _ZL_MATCH_MODE=1
    

    或者使用下面语句:

    eval "$(lua /path/to/z.lua --init bash enhanced)"
    

    进行初始化,他们是等效的,记得把上面的 bash 可以根据你的 shell 改为 zsh 或者 posix

    对于一个给定的正则关键字序列(即 z 命令后面的参数),某路径有且只有满足下面两个条件才算作 “匹配成功”:

    1. 正则关键字将按顺序进行匹配(这条和默认匹配法相同)。
    2. 最后一个关键字可以和路径名的最后一段相匹配。

    如果两条规则同时启用找不到任何结果,那么将会退回到只用规则 1 进行筛选,这两条规则是参考 fasd 引入的。

    • 匹配路径名的最后一段:

      假设数据库内容为:

      10   /home/user/workspace
      20   /home/user/workspace/project1
      30   /home/user/workspace/project2
      40   /home/user/workspace/project3
      

      在增强模式下使用 "z wo" 的话,只有 /home/user/work 满足匹配,因为按照第二条规则,这是唯一一条最有一段名称匹配 wo 的路径。

      因为最后一级目录名称总是最容易记住的,所以给到它比较高的优先级。在默认匹配算法中,你同样可以用 "z space$" 来达到相同的目的,但是 "z wo" 可以打更少的字。

      小技巧:

      • 如果你在增强匹配算法下,想让最后一个关键字不当匹配最后一段路径名,还可以像默认匹配算法中一样匹配路径的其他部分的话,你可以在最后加一个独立的 '$' 参数,比如:"z wo $"
      • 如果你在增强匹配算法下,想让最后一个关键字匹配最后一段路径名以前的部分,那么可以增加一个斜杆参数,比如:"z wo /"
    • 如果没法匹配,同时又存在一条路径名和关键字相同,那么 cd 过去:

      有时候如果你输入:

      z foo
      

      但是数据库里又没有任何匹配 foo 的记录,然后却存在一个可以在当前位置访问的目录,刚好名字是 "foo",那么 "z foo" 的效果将会和下面的命令效果相同:

      cd foo
      

      因此,在增强匹配算法中,你总可以象 cd 命令一样使用 z 命令,而不必当心目标路径是否被记录过。

    • 忽略当前路径:

      如果你使用 z xxx 但是当前路径恰好是最佳匹配结果,那么 z.lua 会使用次优结果进行跳转。假设有如下数据:

      10   /Users/Great_Wall/.rbenv/versions/2.4.1/lib/ruby/gems
      20   /Library/Ruby/Gems/2.0.0/gems
      

      默认情况下,当我使用 z gems 时,我会被带到 /Library/Ruby/Gems/2.0.0/gems,因为它有更高权重,但是可能并不是我想要去的地方,这时我按一下方向键上键,再次执行 z gems,那么我就能被带到 /Users/Great_Wall/.rbenv/versions/2.4.1/lib/ruby/gems 目录中,而这正是我想去的地方。

      我当然可以每次使用z env gems 来精确指明,但是每当我输入 z xxx 我必然是想进行路径跳转的,而不是呆在原地,所以使用增强匹配模式,即便当前目录是最佳匹配,它也能懂得你想跳转的心思。

    再我最初实现 z.lua 时,只有一个和 z.sh 类似的默认匹配算法,在网友的建议下,我陆续学习了来自 fasd / autojump 中的优秀理念,并加以完善改进,成为如今集三家之长的 “增强匹配算法” ,给它取个昵称,叫做 “更懂你的匹配算法”。

    更新数据库的时机

    何时更新数据呢?默认情况下,z.lua 会在每次显示命令提示符时记录当前路径(和 z.sh 一致),但是还提供了一个 $_ZL_ADD_ONCE 的环境变量选项,设置成 1 的话,只有当前路径改变,才会将新路径添加到数据库。

    除了设置环境变量外,不同的 shell 下还可以在初始化时增加 "once" 参数来达到相同目的,比如:

    eval "$(lua /path/to/z.lua --init bash once enhanced)"
    eval "$(lua /path/to/z.lua --init zsh once enhanced)"
    lua /path/to/z.lua --init fish once enhanced | source
    

    将会同时启用增强匹配算法和 once 机制,在一些比较慢的硬件下(路由器,cygwin,msys ),使用该机制将有效的提升性能。其实 autojump 在 zsh 下会使用类似 once 的机制,而 bash 下则和 z.sh 类似。

    从效果上来讲,z.sh 的模式(关闭 once )强调的是 “在某路径下工作的时间长短”,而 autojump 的模式(启用 once )则强调 “进入某路径的次数多少”。

    交互式选择模式

    使用 -i 参数进行跳转时, 如果有多个匹配结果,那么 z.lua 会给你显示一个列表:

    $ z -i soft
    3:  0.25        /home/data/software
    2:  3.75        /home/skywind/tmp/comma/software
    1:  21          /home/skywind/software
    > {光标位置}
    

    然后你按照最前面的序号输入你想要去的地方,比如输入 3 就会进入 /home/data/software。如果你不输入任何东西直接按回车,那么将会直接退出而不进行任何跳转。

    Tips

    推荐一些常用的命令别名:

    alias zc='z -c'      # 严格匹配当前路径的子路径
    alias zz='z -i'      # 使用交互式选择模式
    

    同时你可以定义一个名为 zf 的命令,搭配 fzf 进行历史路径模糊匹配:

    alias zf='cd "$(z -l -s | fzf --reverse --height 35%)"'
    

    性能评测

    最慢的部分当然是添加当前路径到数据库。该操作会在每次你按回车时执行,所以我在我的 Nas 上做了个对比:

    $ time autojump --add /tmp
    real    0m0.352s
    user    0m0.077s
    sys     0m0.185s
    
    $ time fasd -A /tmp
    real    0m0.618s
    user    0m0.076s
    sys     0m0.242s
    
    $ time _z --add /tmp
    real    0m0.194s
    user    0m0.046s
    sys     0m0.154s
    
    $ time _zlua --add /tmp
    real    0m0.052s
    user    0m0.015s
    sys     0m0.030s
    

    可以看出,z.lua 是消耗资源最少,并且最快的,可以更流畅的在性能不好的环境中使用。

    结论

    真的可以卸载 autojump / z / fasd 了。

    第 1 条附言  ·  352 天前
    更新版本:43 解决随机数安全问题,以及 fish shell 下 interactive selection 无法工作的问题
    40 回复  |  直到 2019-11-08 12:21:13 +08:00
    Chingim
        1
    Chingim   352 天前 via Android
    看起来不错,没想到我用的 fasd 这么不能打
    这个快是因为 lua 语言(fasd 应该是 shell script 实现)带来的吧?
    Chingim
        2
    Chingim   352 天前 via Android
    我找到一个能打的 https://github.com/xen0n/autojump-rs/blob/develop/README.md

    能加入比较么
    zhs227
        3
    zhs227   352 天前
    最近在尝试楼主的这个东西,用 luajit 加持一般进入目录的时间在个位数毫秒级别,非常好用
    yuikns
        4
    yuikns   352 天前
    点开工程发现用户名有点熟悉。原来好久前就关注了。
    膜大佬
    ynyounuo
        5
    ynyounuo   352 天前
    @Chingim fasd 基本上没有后续支持了,作者感觉忘记了这个项目
    skywind3000
        6
    skywind3000   352 天前
    @Chingim z.lua 更快,切换路径只要 0.017 秒,你看他的评测他居然需要 0.050 秒。其实也不奇怪,lua 的可执行也才 200 多 KB,它可执行 5MB,strip 过后也有 1.5MB ,启动速度它就拜下来了,他还没启动完,z.lua 可能都运行完了。
    yuikns
        7
    yuikns   352 天前
    好奇怪,我 mac 的 bash 使用了下, lua 5.3.5,随便 cd 几下后,在 /tmp 下产生了大量 lua_xxxxxx 空文件。

    data_save 那个 function 好像会删除文件,但是失败了?

    请问发 issue 还需要什么信息?
    wweir
        8
    wweir   352 天前 via Android
    @skywind3000 可执行文件在文件系统是有缓存的,以这点文件大小来说事并不科学。
    如果想从文件大小来说明比 rust 的实现快,至少得冷热启动分别给一下数据
    wzw
        9
    wzw   352 天前 via iPhone
    看了就想试试,稳定性如何
    skywind3000
        10
    skywind3000   352 天前
    @wweir 不是说它要载入这个大文件费时间,而是文件那么大,初始化一定很多代码要跑。
    wweir
        11
    wweir   352 天前 via Android
    @skywind3000 同样,进程自身在内核中也是有缓存的,重置一下上下文就好

    不想去扣这些底层,对普通人来说,就是玄学。要表述一个违反常规认知的观点,那就来点实际的吧,比如:bench 数据
    congeec
        12
    congeec   352 天前
    为嘛用 lua 呢?为了性能?
    Yggdroot
        13
    Yggdroot   352 天前 via Android
    正在使用,很好用。z.lua 还有个优点,就是有什么好的想法作者都会很快做出反馈,其它的工具就未必。
    guanhui07
        14
    guanhui07   352 天前
    不错
    clown139880
        15
    clown139880   352 天前
    mac 用 antigen 安装之后报错
    skywind3000/z.lua/z.lua:1414: attempt to concatenate a nil value
    屏蔽两行后使用 z 提示_zlua:33: permission denied
    是我 lua 的问题么,我用 brew install lua 安装的
    使用 z.sh 时没有出现过任何问题
    dltsgl
        16
    dltsgl   352 天前
    centos6 报错:
    lua: /home/v33491/download/lua/z.lua:7: unexpected symbol near '<'

    lua version 是: Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio

    用的 bash

    不太懂,请教一下什么原因
    lihongjie0209
        17
    lihongjie0209   352 天前   ♥ 2
    这种交互式的工具性能倒是其次, 关键是易用性
    skywind3000
        18
    skywind3000   352 天前
    @clown139880 重新按默认参数编译个 lua 吧,brew 的版本有问题。
    skywind3000
        19
    skywind3000   352 天前
    @wweir 如你所愿,我做了个测试:

    原版本 autojump:

    [email protected]:~/.vim$ time j vim
    /home/skywind/software/vim

    real 0m0.149s
    user 0m0.047s
    sys 0m0.063s

    autojump-rs:

    [email protected]:~/software/vim$ time j vim
    /home/skywind/.vim

    real 0m0.075s
    user 0m0.000s
    sys 0m0.031s

    z.lua:

    [email protected]:~/.vim$ time z vim

    real 0m0.019s
    user 0m0.016s
    sys 0m0.016s

    够明白了么?我说的启动时间不仅指操作系统层的启动时间,还指应用程序自身的初始化时间,程序越庞大越复杂,模块越多,自然涉及到越复杂的启动过程,举个例子,就像 C++ 全局对象多了以后,进入主程序之前,都有一大堆构造函数要调用。
    wweir
        20
    wweir   352 天前
    @skywind3000 数据依然不够全面,benchmark test 报告,可不是这么简单就行的
    skywind3000
        21
    skywind3000   352 天前
    @wweir 我已经给出支持我论据的初步测试了,你如果觉得不妥但,欢迎进一步测试来反驳。
    wweir
        22
    wweir   352 天前 via Android
    我反驳啥?我只要提出质疑就行了。
    谁让你是想推广的那一方的,我们作为用户,就是有这个特权 😄
    Yggdroot
        23
    Yggdroot   352 天前 via Android
    @wweir 质疑也要有根有据,而不是无脑喷。
    haozhang
        24
    haozhang   352 天前 via Android
    @wweir 不要包括我,我没那么厚的脸,被打肿了还要充胖子。
    uyhyygyug1234
        25
    uyhyygyug1234   352 天前
    感觉切换目录这个,fasd z.sh 性能已经挺好了。不过路由器上可能差点。

    我见到 https://www.v2ex.com/t/436485 这边有配合 fzf 做交互的的选择,更加直观。




    ps 楼主的文章介绍更好的 init.sh 中,提到的“有恒产才会有恒心”想法很好。
    skywind3000
        26
    skywind3000   351 天前
    @yuikns 该问题已经修复,初始化随机数种子用了一个 os.tmpname() 结果没发现该函数会在 /tmp 建立临时文件。随机数种子已经改写为更好的方式了,不再调用 os.tmpname(),该问题修正。
    skywind3000
        27
    skywind3000   351 天前
    @uyhyygyug1234 其实下面方法也可以代替:

    alias zf='cd "$(z -l -s | fzf --reverse --height 35%)"'

    达到你说的这个效果,把 z.sh/z.lua 的历史数据拿出来,放给 fzf 匹配。但是啊,z.lua 的匹配不当会考虑字符串,还会考虑 frecency 权重,不是简单 fzf 那种字符串排序。同时还有很多用于提升效率的匹配规则,也不是 fzf 那种纯字符串匹配。
    skywind3000
        28
    skywind3000   351 天前
    @dltsgl 能不能自己编译个完整的 lua,有问题再给我反馈。
    有些包管理里面的 lua 编译参数都没给全,导致库函数缺胳膊少腿的。
    Kobayashi
        29
    Kobayashi   351 天前 via Android
    据我所知,你测试中的 fasd 不准确,应该是 autojump 调用 Python 解释器最为耗时。我是从这里看到的:

    https://reddit.com/r/linux/comments/agkx07/zlua_a_better_method_to_change_directory/eecu8op?context=3

    作者有考虑像 fasd 一样从输入的命令中提取路径信息、文件信息吗?

    @uyhyygyug1234 autojump, z, fasd, z.lua, zsh-z 对比可以看看上边的链接。从结论上讲,fasd 因为抓取了输入的命令中的路径,考虑的要比其他几位多。
    但从速度上来看,z.lua 和 zsh-z 最快,前者支持 POSIX sh,但后者也说明其实慢的不是 shell 脚本,而是因为 z 中调用外部命令耗时。
    uyhyygyug1234
        30
    uyhyygyug1234   351 天前
    uyhyygyug1234
        31
    uyhyygyug1234   351 天前
    貌似 z[dot]sh 这个触发了外链 spam 规则。只能贴图了。
    GPU
        32
    GPU   351 天前
    我用 oh my zsh 的话怎么安装这个插件

    我在 .zshrc 里面 加入 plugins=( skywind3000/z.lua ) 这样不生效 .

    还是这种方式只支持 antigen
    skywind3000
        33
    skywind3000   351 天前
    @GPU 我不用 oh-my-zsh 啊,试试这个:

    git clone https://github.com/skywind3000/z.lua $ZSH_CUSTOM/plugins/zlua

    然后配置里:
    plugins=( ... zlua )
    Kobayashi
        34
    Kobayashi   351 天前 via Android
    @uyhyygyug1234 关于 fasd 速度啥不是对你说的,是想问作者有没有支持 fasd 做法的计划。z.lua 替换 z 和 autojump 是可能,替换 fasd 就算了。z.lua 既没有 v 的功能,也没有从命令行提取路径的特性,比不上 fasd。

    fzf 我自己也在用,fzf 的 wiki 有列出你给出的脚本,不过还是谢谢了。好像我记得 fzf 加入--height 参数可以把 fzf 菜单移动到光标下,而不是全屏,我个人喜欢这么干。
    Kobayashi
        35
    Kobayashi   351 天前 via Android
    @skywind3000 我猜作者以前没写过 ZSH 插件,也不用 ZSH 框架、插件管理器?插件脚本名应该与插件所在文件夹名、项目名一致。即把插件脚本改名为 z.lua.plugin.zsh ,这基本上已经成为 ZSH 插件的规范要求。现在的命名根本没办法被 oh-my-zsh 加载,大部分插件管理器也加不了。

    只要想问一下作者,有没有考虑给 z.lua 加入类似 fasd 的命令内容提取?
    skywind3000
        36
    skywind3000   351 天前
    @Kobayashi 我一直用 antigen,只要后缀有 .plugin.zsh 它就识别了,没用 oh-my-zsh,已经改成 z.lua.plugin.zsh 了。关于 fasd,我感觉它这种疯狂收集所有路径的开销太大,我几台 nas / 路由 和 cygwin/wsl 根本吃不消,用不起。暂时不是 z.lua 的目标,未来我会花时间增加更多便利的跳转机制,以及 fzf 的集成。
    Kobayashi
        37
    Kobayashi   351 天前 via Android
    @skywind3000 恩,我确实没有考虑到在路由上的消耗问题,谢谢解答。
    a132811
        38
    a132811   348 天前
    lua ${HOME}/conf/z.lua --init zsh
    ZLUA_SCRIPT="usage: which [-as] program ..."

    报错了呢?
    skywind3000
        39
    skywind3000   348 天前
    @a132811 你的 lua 有问题,是不是 brew 安装的啊? z.lua 需要取得 zlua 可执行的路径,结果取不出来。重新按默认参数编译一个 lua 解决。
    stdout
        40
    stdout   72 天前
    果断换成 z.lua 了,fasd 太慢了。按这 enter 不动明显能感觉出来。
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2576 人在线   最高记录 5168   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 38ms · UTC 12:44 · PVG 20:44 · LAX 04:44 · JFK 07:44
    ♥ Do have faith in what you're doing.