【问题标题】:zsh command line editor: go back to previous lines when entering a commandzsh 命令行编辑器:输入命令时返回上一行
【发布时间】:2015-06-14 23:41:32
【问题描述】:

请注意,我不是在谈论历史上的前几行。我说的是当前多行命令中的前几行。

例如,当我在 zsh 中键入多行命令时(_ 表示光标):

$ for i in {1..5}; do
for> echo i_

此时我可能会改变主意,想让i 循环通过{1..10},所以我需要回到上一行。但是,我无法弄清楚如何做到这一点,如⌫、←、或C-b,或者我能想到的所有内容都在第二行的开头停止。那么,到底有没有可能搬回来呢?提前致谢。

其实这不限于zsh。例如,我也从未在 bash 中解决过这个问题。

我已经花了相当长的时间在谷歌上搜索,但没有任何发现,所以如果这很明显,请原谅我并责怪我的 Google-fu。


供您参考,我使用的是 Emacs 键绑定,并且

$ bindkey | grep delete
"^D" delete-char-or-list
"^H" backward-delete-char
"^[[3~" delete-char
"^?" backward-delete-char

不确定这是否会有所帮助。

【问题讨论】:

  • 我认为这至少在bash 中是不可能的,但不确定zsh。问题是一旦你在输入第一行后点击Enterbash 会记住该行但不再接受来自该行的输入。
  • 如果你想经常编辑多行命令而不把它们放入shell脚本,至少在bash,如果你是vi模式,你可以输入v打开路径存储在$VISUAL 中的编辑器。我不确定 emacs 键绑定的等效项。
  • @merlin2011 感谢您的信息。我对vi 不是很熟悉,请您解释一下“输入v 打开编辑器...”吗?我的意思是,我究竟要输入什么来打开编辑器(肯定不是一个 v,是吗)?
  • 我会写这个作为答案,不是因为它解决了你的问题,而是因为我需要多行。
  • 对于那些投票接近的人:这个问题关于编程的,至少是切线的。此外,还有大量关于 SO 的命令行编辑问题;如果您坚持,最好先关闭它们。

标签: shell command-line zsh


【解决方案1】:

这是在bashvim 模式下的visual 模式。

.bashrc 中放入以下行。

set -o vi

# I do not remember which one of these is used by `v`, so I set both
export VISUAL=/usr/bin/nano
export EDITOR=/usr/bin/nano

重新加载bash,然后按Esc 进入normal 模式。 键入v,并应加载nano。您现在可以在nano 中输入您的多行命令。保存退出时会执行多行命令。


对于zsh,您的rc 中的以下几行可以做到这一点。我从this answer 中窃取了最后三行并在我的zsh 上进行了测试。

bindkey -v
autoload -U edit-command-line
zle -N edit-command-line
bindkey -M vicmd v edit-command-line

【讨论】:

  • 这实际上是一个不错的答案。它解决了一个可能的 XY 问题。
  • @Qix,问题是他在询问zsh,这描述了bash 的解决方案。我想 OP 会告诉我们这是否有助于他的处境。 :)
  • 你能在zsh找到类似的解决方案吗?我还没有遇到bash 可以做到zsh 做不到的事情。
  • 感谢您的回答!它确实有效。不过,我将继续寻找 Emacs 解决方案(不幸的是 Emacs 是我的最爱)。顺便说一句,bash 代码中有一个错字:应该是 export VISUAL 而不是 export $VISUAL
  • @Qix 你知道export -f 的zsh 方式吗?这是我切换到 zsh 后遇到的第一个障碍,直到今天我还没有找到解决方案。这就是为什么当我需要 GNU Parallel 之类的东西时,我会编写 bash 脚本而不是 zsh 脚本。
【解决方案2】:

其实这是可能的。

在第二行,按下 Esc + X 以执行命令, 并输入push-line-or-edit(注意可以使用Tab键补全) ,然后按return

提示将更改为传统提示,将您的 for> 部分从 续行,你将有一个新的缓冲区充满你所有的 以前键入的行,当然现在您可以使用 well 轻松编辑 已知C-aC-b 键。

【讨论】:

  • 是的。而push-line-or-edit 通常绑定到C-qM-q(我相信),或者可以使用bindkey 设置自定义键绑定。 (当我问这个问题时,我完全是 ZLE 的新手。)
  • @kYuZz 这是一个很好的答案,正如我在上面的评论中指出的那样,在发布答案时我已经弄清楚了(但“通常绑定”部分在技术上是不正确的; 默认情况下它是未绑定的,但 Prezto 将它绑定到 M-q,隐藏 push-line)。但是,我认为在授予答案一年多后取消对答案的接受是不礼貌的。如果您喜欢这个答案,请点赞。
【解决方案3】:

我认为这是不可能的,但这里有其他解决方法:

  1. 您可以将所有内容写在一行中。它不容易编辑,但多行字符串也不容易编辑。
  2. 你可以绑定一些键(例如<C-Enter>,如果你可以告诉你的终端在这种情况下不要输出<C-m>)到

    function _-add-newline()
    {
        LBUFFER="$LBUFFER"$'\n'
    }
    zle -N add-newline _-add-newline
    bindkey "\Ca" add-newline
    

    。这样,如果您按<C-a>,换行符将出现在缓冲区中,但不会触发接受行小部件,并且上一行仍然是可编辑的。你可以例如使用左/右箭头移动到它。

  3. 如果您键入for x in y ; do<CR>echo foo<CR><C-c><Up>,您将在缓冲区中看到for x in y ; do\necho foo,并且所有文本都可以编辑。注意:您需要<CR> 保留最后一行(<C-c> 中止的行不保存)和<C-c> 中止输入;如果您已经输入了done,则此变体将不起作用(首先<CR> 将运行循环)。在最后一种情况下,您可以丢弃最后一行 <C-c> 并重新输入。
  4. 理论上你可以用一个代码覆盖accept-line小部件,该代码解析你的输入并确定你是否已经完整地编写了命令,如果没有,那么它只会将\n附加到缓冲区中(就像上面的函数一样)并且不要运行原始accept-line,否则运行原始accept-line。不过,这种变体实现起来要困难得多,所以我说的是“理论上”。

【讨论】:

  • 感谢您的帮助!你的回答对这个问题来说已经足够好了,我接受了。但是,你知道吗,这个问题实际上有点欺骗性。秘密地,我需要回去的最重要原因是有时我不小心过早地敲击了输入;)所以解决方案(1)被排除在外。解决方案(3)是我一直在做的,这导致了丑陋的命令行和肮脏的历史。 (2) 很有趣(我会指出C-a 已经绑定),但仍然不能防止事故的发生。我想总体答案是“不”......我坚持(3)。
猜你喜欢
  • 2011-12-11
  • 2023-04-08
  • 1970-01-01
  • 2013-01-22
  • 2011-10-21
  • 1970-01-01
  • 2013-03-20
  • 1970-01-01
  • 2012-05-08
相关资源
最近更新 更多