是否有一个 shell 可以快速允许重新排序参数?

是否有一个 shell 可以快速允许重新排序参数?

我大量使用命令行,多年来已将其迁移bashzsh日常驱动程序 shell。我通常使用稍微定制的 oh-my-zsh 环境,但有些系统是在 prezto 上的;差异并不大。

我在 zsh 中使用的最高效的插件是zsh-syntax-highlightinghistory-substring-search,最近我一直在使用非常强大的fzf插件来提取历史记录。

现在,我发现命令行中对我来说最大的痛点之一是命令参数重新排序。我经常尝试运行命令

command very/long/filesystem/path/to/argumentA another/filesystem/or/network/path/argumentB

并意识到我把顺序弄反了。

另一种更常见的情况是当我们进行任何“手动部署”工作流程时:首先将新内容与真实内容进行比较,例如

diff /opt/app/static/www/a.html /home/user/docs/dev/src/a.html
cp /home/user/docs/dev/src/a.html /opt/app/static/www/a.html

好吧,好吧,最后一个例子(这个有几个步骤),我保证不会再有更多了。完美的现实世界例子就在这里。让我们开始研究一些具有可爱的人体尺寸的文件列表:

find /pool/backups -type f -print0 | xargs -0 ls -lh > filelisting

我想以交互方式进行大小排序并挑选一些:

sort -rhk5 filelisting | fzf -m > /these/are/the_chosen

很好,这有效!哦,但我需要只是现在的路径,但不想重新运行find

cut -d ' ' -f 10 /these/are/the_chosen

输出是垃圾,因为我们在ls -lh使用空格时遇到了挫折。但我有一个策略:加入连续的空间。让我们选择tr -s压缩空格字符,这里不需要正则表达式。不过,tr需要标准输入:

cat /these/are/the_chosen | tr -s ' ' | cut -d ' ' -f 9- > filelist

至此,我们已经感受到了在命令中剪切和粘贴参数的痛苦。我在这里的选择总是在尴尬的选择之间:我可以移动很长的路径,或者我可以移动命令。无论移动哪一个,我都必须伸手去拿鼠标来复制它,或者我必须在新位置再次输入它。我赢不了。使用命令行,即使导航也很麻烦,在没有设置跳字热键的情况下尤其如此。

我什至无法使用鼠标在 shell 提示符下快速跳转! (嘿,有人知道支持鼠标事件的 shell 吗?)所以,是我想要消除的低效率。我想消除抓取外壳对象(例如有效路径字符串)的摩擦,并在制作怪物管道原型时自由地左右移动它。如果我有这个功能,我可以发送 5 次垃圾邮件,将路径推到 & 标志的左侧cut,然后有机地构建管道的其余部分。

我相信缺乏行编辑能力就是问题所在。在第一个示例中,我想要转置前 2 个参数,我可以创建一个简单的 shell 脚本,也许我会调用该脚本cpto来反转参数并委托给cp命令。但我不想这样做,而且在一般情况下这对我没有帮助,就像第三个例子一样。

我希望能够使用简单的组合键对我输入的参数进行重新排序,就像我在 Vim 中使用以下插件时可以对各种类型的列表所做的那样侧身

zsh 有这样的插件吗?其他 shell 是否存在这样的插件?

如果没有的话,zsh 的实现会有多困难?我认为 zsh-syntax-highlighting 插件证明应该可以对参数进行标记。事实上,shell 知道如何从历史记录中获取单个参数:https://stackoverflow.com/a/21439621/340947

这个痛点是如此严重和普遍,以至于我不得不编写一个简单的脚本来绑定到一个热键,该热键获取历史记录中的最后一个条目并为我交换最后 2 个参数,然后运行它。但这并不像使用行编辑器操作那样理想,以便可以交互式地完成交换,而不是承诺运行命令。

也许对此的改进可以是注入!:0 !:2 !:1(zsh 很好地为我自动扩展),但是这也存在很多问题:(1)如果没有尝试运行错误的命令,它就无法工作。超过一半的时间我想在输入错误的命令后发现自己之后交换参数,并且(2)通常会使用一些该片段无法解释的标志。

我也看到了所示的方法这里这很好,但仍然非常不令人满意,因为长路径需要多次重复击键,并且 Ctrl+Y 行为只能调用最近剪切的项目,而不是保存一堆项目。知道这很好,但对我来说几乎没有用。

为了完整起见,现在采取的策略是使用任何合适的组合键来删除单词,删除较短的命令以重新排序,重新定位光标,使用鼠标从终端输出复制已删除的参数,然后将其粘贴回普通人对此不会眨眼,但每次我这样做都会让我感到有点死,因为我不停地思考计算机为我完成这项任务是多么容易,以及我感到的不公正。必须把手伸到鼠标上。

答案1

在 中zsh,默认情况下运行的所有小部件包括transpose-words默认绑定到Alt+Tinemacs模式的工作,用于定义为 alnum+$WORDCHARS字符序列的单词。

$WORDCHARShas的默认值*?_-.[]~=/&;!#$%^(){}<>,所以包含/,所以应该适合你转置路径只要这些路径不包含之外的字符即可。这对于包含:, @, ,... 或被引用的路径不起作用。

但你可以使用select-word-style框架来改变定义单词一经请求。

如果您添加:

autoload -U select-word-style
zle -N select-word-style
bindkey '\ez' select-word-style

给你~/.zshrc,然后按Alt+Z,你会得到选择:

Word styles (hit return for more detail):
(b)ash (n)ormal (s)hell (w)hitespace (d)efault (q)uit
(B), (N), (S), (W) as above with subword matching
?

按下后“返回查看更多详情”

(b)ash:       Word characters are alphanumerics only
(n)ormal:     Word characters are alphanumerics plus $WORDCHARS
(s)hell:      Words are command arguments using shell syntax
(w)hitespace: Words are whitespace-delimited
(d)efault:    Use default, no special handling (usually same as `n')
(q)uit:       Quit without setting a new style

所以按下S会让你转置两个 shell 单词(因此包括那些包含引号空格或命令替换的单词...) with Alt+T(或用 删除一个Ctrl+W,用 移回一个Alt+B,等等)。

info zsh select-word-style

有关详细信息(假设您的系统上已安装 zsh 文档(zsh-docDebian 及其衍生产品上的软件包))。

您会发现其中有一个部分看起来像是专门为您编写的,您可以对其进行调整以指定transpose-words每当光标位于文件名上或单词之间等时您想要的行为方式:

以下是一些使用单词上下文样式来扩展上下文的示例。

   zstyle ':zle:*' word-context \
          "*/*" filename "[[:space:]]" whitespace
   zstyle ':zle:transpose-words:whitespace' word-style shell
   zstyle ':zle:transpose-words:filename' word-style normal
   zstyle ':zle:transpose-words:filename' word-chars ''

这提供了两种不同的使用转置单词的方式,具体取决于光标是位于单词之间的空格上还是文件名上,这里是包含 / 的任何单词。对于空白,标准 shell 规则定义的完整参数将被转置。在文件名中,只有字母数字会被转置。在其他地方,单词将使用默认样式进行转置 :zle:transpose-words

例如,与:

autoload -U select-word-style
zle -N select-word-style
bindkey '\ez' select-word-style
select-word-style normal
zstyle :zle:transpose-words word-style shell

transpose-words将与单词总是,而所有其他单词小部件将使用普通的的定义单词,并且您仍然可以用来Alt+Z更改它(对于除 之外的小部件transpose-words)。

答案2

我确实意识到 zsh 的 vim 模式比我之前意识到的要先进一些。有一个a与参数相对应的文本对象。因此,我可以通过输入诸如 之类的序列来利用一些肌肉记忆EscdaaBPiSpace。但您也可以看到 8 次击键远非理想。鉴于此操作需要插件才能在 Vim 中优雅地实现,对于 zsh 行编辑器来说也是如此。

此外,将 zsh 置于 vim 模式确实会破坏我已有的绑定,尽管它们应该很容易复制。有时,在 shell 中拥有 vim 绑定确实会很好。但我仍然倾向于这样的立场:必须处理可能处于插入模式的 shell 并不值得这么麻烦。

答案3

我多次遇到类似的问题,并且不相信有一个干净的解决方案可用。您应该能够进行一些黑客操作来完成此任务。最黑客的方法可能是直接将命令转储到历史记录中,而不通过在命令前面加上 echo 前缀然后丢弃 !:0 来执行它,之后您的上述解决方案就可以工作了。

更好的方法是连接到 ZSH 版本的 readline(如果我没记错的话,是 zle)。它比常规的 readline 强大得多,您可能可以就地操作它,而无需将错误的命令写入历史记录。我建议查看现有的插件,这些插件可以自动就地扩展该行作为起点。一个很好的例子是这个插件:https://github.com/wazum/zsh-directory-dot-expansion(当您键入时会扩展...为)。../..事实上,查看该存储库,您可能可以通过更改作者原始脚本中的 1-2 行来获得此逻辑的插件。

请注意,您不会zle在用户键入时自动展开,而是对某些用户按键序列做出反应(类似于alt+T您提到的组合)。因此,您的插件将更类似于制表符补全,而不是简单的自动扩展。

答案4

为了解决您提到的有关路径的具体情况,您不需要使用复杂的“切换顺序”,您需要最近的路径历史记录管理,例如 fzf 来模糊搜索历史记录。 Zsh archwiki 应该提到 dirs,你应该检查一下。

我发现我经常 cd 到一条路径,然后需要对该路径内的某些内容执行一些操作,而当前我不在该路径中。路径历史管理将像 fzf 一样提高效率。

相关内容