Bash:如何使用自定义键盘快捷方式调用脚本?

6
假设我有一个脚本 "myscript.sh",它的内容只是 echo $PWD。我想在 bashgnome-terminal)中将此脚本绑定到某个键组合上,这样当我按下这个键组合时,"myscript.sh" 的输出将被插入(“粘贴”)到终端中光标的位置。
显然,bash 历史记录和行操作由 readline 处理,而我得到的有关 bash 键盘快捷键的参考资料确实引用了 readline 我还在 Bash 参考手册:Readline Init 文件语法 中看到,可以使用 bind -p 列出 bash 的键绑定(更多信息请参见 help bind [不是 'man bind'])。所以,也许这个问题更好地称为“在 readline 中将宏绑定到自定义键盘快捷键”:) 但无论如何,我想知道我想要的是否可能实现?
我猜另一种选择是将脚本设置为 "pwd | xsel -b",然后在终端中调用它,然后可以粘贴;但我仍然希望有一个单一的键盘快捷键,比如 Ctrl-Alt-H(似乎没有用于任何东西),当按下时立即插入/粘贴脚本输出。
提前感谢您的帮助!
svn ci -m "myproject-folder-0012a: here a commit message"

但这正是我不喜欢的——首先我打出11个字符,这些字符相当快:

svn ci -m "

然后,我无法使用自动完成获取名称(我在文件夹内)-这意味着我要么必须完全输入它(没有办法:),要么从提示中复制粘贴它(这需要选择-按鼠标,拖动,释放鼠标;然后Ctrl + Shift + C,然后Ctrl + Shift + V,再加上任何左/右键,如果我错过了对齐-再加上删除等,如果我复制错误)。
这意味着为了得到该死的提交消息的该死的文件夹名称,要做这么多工作:(我更愿意按下类似于Ctrl-Alt-H的某些键,然后将文件夹名称自动插入到光标位置,并完成它:)
我对xsel的建议仅是因为我可以将其放入“全局”脚本中-比如将其作为符号链接到/usr/bin/myscript(显然,脚本的内容是echo $(basename $PWD)而不仅仅是pwd,以满足我的需求),然后我可以执行:
$ myscript       # this puts directory name in clipboard
$ svn ci -m "[CTRL+SHIFT+V TO PASTE HERE]myproject-folder-0012a[NOW TYPE]: here a commit message"

...这在某种程度上减轻了工作量,但我仍然需要记住脚本名称并调用它,输入svn命令之前(而我并不总是记得...)而且我仍然需要调用一个命令,然后按下一组键;为什么我不能只按一组键,就完成了呢?!:)

好的,希望这更清楚地阐明了我的问题....

 
EDIT2:然而,另一个使用bash键盘快捷键有用的原因是,我还可以在终端程序中“粘贴/插入当前目录名称”,比如像nano这样的程序(在那里,直接使用bash脚本或函数扩展可能更加困难)。


2
听起来像是适合SuperUser的问题。 - Sebastian Paaske Tørholm
1
你不需要执行“pwd | xsel -b”和粘贴组合。如果你使用反引号将某些内容包围起来,那么从反引号命令的输出将被作为被执行命令的输入。 - Reese Moore
嗨,Reese,我不确定反引号如何适用于我的问题,所以我尝试在上面的编辑中澄清我的问题...这就是关于输入更少的一切 :) - sdaau
3
作为一种替代方案,可以使用以下Shell函数:svnci() { svn ci -m "$PWD: $1" "${@:2}" ; }。在使用时,输入$ svnci "提交信息" <其他svn参数> - camh
也许这是一个愚蠢的问题,但是... 你为什么要在提交信息中放置$PWD呢?我从来没有使用过svn,但至少对于git来说,这将是相当无意义的。 - MestreLion
显示剩余3条评论
3个回答

10

简单版:

在命令行提示符下执行以下命令:

bind '"\ee": "${PWD##*/}\e\C-e"'

或者将以下这行代码添加到你的~/.inputrc文件中:

"\ee": "${PWD##*/}\e\C-e"

按下 Alt-e 会在命令行中插入当前目录的基本名称。这需要 readline 函数 shell-expand-line 的默认绑定为 \e\C-e(如果不同可以进行调整)。我还假设您使用的是 Bash 的 Emacs 模式。

不幸的是,它也会扩展已经输入的内容。其中之一的影响是,在输入:

svn ci -m "

按下Alt-e后,引号将不见了。有几种方法可以解决这个问题。

第一种是,假设你只丢失了引号,你可以手动添加回去,或者让readline宏自动为你添加:

bind '"\ee": "${PWD##*/}\e\C-e\eb\"\C-e"'

这种方法并不是很令人满意。

高级版:

或者,先删除该行,进行插入,然后再将该行复制回来:

bind '"\ee": " \C-u \C-a\C-k${PWD##*/}\e\C-e\C-y\C-a\C-y\ey\b"'
或者
bind '"\ee": " \C-u \C-a\C-k${PWD##*/}\e\C-e\C-y\C-a\C-y\ey\b\ef\C-f"'

这将保留行的其余部分不变(不会扩展或删除任何内容),但它使用了kill环,所以可能会使其处于与您期望不同的状态(如果您正在使用它)。它还会在插入的目录名后面插入一个空格(宏中的空格用于确保在行的开头或结尾执行宏时不会重新生成较旧的kill-ring内容)。无论光标在行中的位置如何,该宏都应该能够正常工作。在第一个版本中,插入将在光标的位置进行,光标保持在相同的位置。

编辑:第二个版本在插入目录名和空格后将光标放置在那里。

编辑2:

readline函数shell-forward-word(未绑定)对此有更好的处理方式,比forward-word\ef)更好。您可以像这样使用它:

bind '"\ew":shell-forward-word'
bind '"\ee": " \C-u \C-a\C-k${PWD##*/}\e\C-e\C-y\C-a\C-y\ey\b\ew\C-f"'

顺便提一下,你应该知道Bash键盘快捷键在其他程序中(如nano)不起作用。


以这种方式进行操作,可以在命令行上提供功能,而无需修改和维护多个完成函数。 - Dennis Williamson
哇,Dennis,感谢你的精彩回答(我自己从文档中读不懂这个)。有几件事情:首先,对于gnome-terminal来说,Alt-e不是一个好选择——因为它绑定了打开编辑菜单;虽然Alt-o可用。我刚刚尝试在shell提示符下使用Alt-e进行“bind”,但什么也没发生;而且bind -P没有列出Alt-e。然后,我在新的~/.inputrc中添加了"\eo": "${PWD##*/}\e\C-e",启动了一个新的终端,Alt-o可以工作(注意:你的输入以'开头——这将键'绑定到命令,而不是Alt-e!)。但是,它有效了——谢谢! - sdaau
PSSS:刚刚尝试了禁用和重新启用~/.inputrc(_mv ~/.inputrc ~/_inputrc并重新启动终端),现在可以看到,当没有定义宏时,Ctrl+Alt+Left/RightArrow正常工作(即跳过一个单词)-但是当启用宏时,这个键组合不会跳过单词,而是生成";5C;5D"。 - sdaau
PSSSS:好的,将条目粘贴到“sudo nano /etc/inputrc”而不是“~/.inputrc”中——保留Ctrl+Alt+左/右箭头,并与新绑定的“shell-forward-word”一起使用……不错! :) 再次感谢,干杯! - sdaau
1
@sdaau:抱歉关于单引号的错别字。请注意,man readline说如果存在~/.inputrc,则会读取它。只有当不存在时,才会读取/etc/inputrc。您可以通过使~/.inputrc的第一行为$include /etc/inputrc来将后者包含在前者中,然后进行覆盖和添加。这是一个很方便的命令,用于查找未绑定的按键:bind -psv | LC_COLLATE=C sort | less - Dennis Williamson
显示剩余2条评论

0

好的,这并不是一个答案,但我想总结一下到目前为止我得到的评论,这对我的问题很有用。然而,就目前而言,关于bash键盘快捷方式运行任意脚本的问题仍未得到解答(我仍然希望用一个组合键完成所有操作 :)

 
首先,我可以使用一个“全局”脚本,例如:

$ sudo bash -c 'cat > /usr/bin/bpwd <<EOF
#!/bin/bash
basepwd=\$(basename \$(pwd))
echo -n \$basepwd                 # suppress line ending
# exec 1>/dev/null               # debug: redir stdout to null
echo -n \$basepwd | xsel -i -b    # suppress LF, and make xsel read from stdin 
# exec 1>/dev/tty                # debug: restore stdout
EOF
chmod +x /usr/bin/bpwd'

或者,我可以在我的.bashrc中添加bash函数(注意:在将这些行添加到.bashrc后,请确保重新加载bash - 例如,通过在当前终端中键入bash):

$ echo '
bpwd2() { basepwd=${PWD##*/} ; echo -n $basepwd | xsel -i -b ; echo -n $basepwd ; }
svnci-test() { echo -n "$(bpwd2): $*" ; }
svnci-m() { svn ci -m "$(bpwd2): $*" ; }' >> ~/.bashrc

 
基本上,我最初误解了Reese Moore的建议 - 你确实可以使用反引号 - 考虑这个命令会话(在上面的命令运行之后):

$ bpwd
Desktop\
$ bpwd2
Desktop\
$ echo `bpwd`
Desktop
$ echo "`bpwd2` 2"
Desktop 2

这就是我需要理解 Moore 的“反引号命令的输出将用作执行命令的输入”(但是,还需要注意清除输出中的行尾);或者,在我的情况下,我可以调用
svn ci -m "`bpwd`: my message here"
# svn ci -m "${PWD##*/}: my message here" # alternatively

...或者,我可以遵循camh的建议,将svnci-m用作函数(在我的情况下,我几乎从不使用额外的参数来调用svn ci,因此我的版本略有不同)。为了测试参数是否正确传递,我可以使用svnci-test函数:

$ svnci-test "my message"
Desktop: my message\

谢谢大家迄今为止的评论,
干杯!


0

要用单个按键完成你想要的操作,一种方法是利用Bash中的可编程完成功能。你可能已经使用了bash_completion工具/包来设置一些可编程完成。如果没有,请了解一下如何完成。

思路是让可编程完成识别svn提交消息开头处的@符号并返回一个单个的补全项,这个补全项是你想要插入的文本(当前目录的基本名称)。

我只尝试过可编程完成,无法为你提供细节,但上述bash_completion软件包或subversion completion script可以作为一个良好的起点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接