我花了整整一周的时间在网上进行搜索,并尝试了多种解决棘手问题的方法。基本上,我想使用vim编辑$ PATH中的自定义命令/脚本,而不必先实际cd到其给定目录或手动在命令行中键入它们的完整路径。
本质上,当将$ PATH中的脚本指定为vim文件参数时,我希望能够将stock bash命令完成(compgen -c)与同时路径扩展结合起来。顺便说一句,我正在用帽子来阐明什么是棘手的话题,而不是大喊大叫。
向您展示我正在尝试做的事情然后解释它可能会更容易。可以说我在$ PATH上的目录中有脚本
~/bin/x/y/cmd1.sh
~/bin/a/b/cmd2.sh
/ppp/n/m/cmd3.sh
有时这些脚本为存在于其他目录中的文件提供功能,因此我希望能够从文件系统中的任何位置轻松对其进行编辑。有时我只是想能够从其他目录编辑那些脚本,因为它更方便。可以说我当前在以下目录中。
/completely/different/dir
但是现在我需要进行vim编辑
~/bin/a/b/cmd2.sh
我唯一要使用默认bash功能实现此目标的选项是执行以下一项耗时较长的操作
cd ~/bin/a/b/; vim cmd.sh
vim ~/<tab-complete-my-way-to-file>
open a new terminal window plus some combination of the above
因为我知道我的自定义脚本的名称,所以只需执行以下操作就容易得多,这不需要对文件或目录的完整路径进行制表符补全,也不需要对其他目录进行cd更改我的情况!!!
vim cmd2.sh
但这在默认情况下不起作用b / c vim需要脚本的完整路径
我的第一个想法是编写一个vim包装函数,该函数基本上使用which
为我执行$ PATH扩展,然后将bash命令完成与我的vc函数绑定在一起,如下所示:
vc () { vim $(which "$@"); }
complete -c vc
我可以在外壳程序中运行以下命令,以完成cmd1.sh, cmd2.sh, cmd3.sh
vc c<tab>
直到我得到我想要的东西,这很棒
vc cmd2.sh
当我按Enter键并执行命令时,一切正常,但不会将扩展路径注入READLINE命令行中,因此'cmd2.sh'的完全扩展路径永远不会在我的命令中消失历史!我的历史将显示此内容
vc cmd2.sh
代替
vc ~/bin/a/b/cmd2.sh
或 vim〜/ bin / a / b / cmd2.sh
我希望在命令历史记录中使用扩展路径,因为在重用命令历史记录时,该脚本文件的将来操作非常容易。即我可以ls,file,diff,mv,cp来扩展路径,而重用历史记录要比为ls,file,diff,mv,cp等编写更多包装器脚本容易得多。就像我上面的vc一样。
问题:
选项1
有一种方法可以将我的vc函数中由which
提供的完整扩展路径直接重新注入到原始vc READLINE中,或者仅注入实际上得到的整个“ vim”命令在vc中执行,以代替进入READLINE的原始vc命令?任何可以使我将扩展的vim命令添加到历史记录中的方法,即使它是对原始vc命令的补充,也可以。
基本上,您如何在bash中以编程方式访问和编辑当前READLINE?
选项2
请注意,我也可以在命令行上直接直接执行类似的操作
vim $(which cmd2.sh) C-x-e
这给了我我想要的(它扩展了将其放入历史的路径),但是我必须始终在每次迭代时都键入额外的子外壳以及文本和Cxe(以扩展行)。命令,同时失去了命令完成功能,这基本上使该命令无用。换句话说,是否有使用绑定键自动执行上述操作,以便
vc cmd2.sh
首先自动转换为
vim $(which cmd2.sh)
,然后自动跟进C-x-e
,以便将其扩展到
vim ~/bin/a/b/cmd2.sh
但是所有编辑动作,文本插入和最终命令行扩展是否都发生在同一个bindkey宏中?这可能是最好的解决方案。
选项3
或者,由于bash命令完成自动在READLINE中自动结束,因此历史记录也自动结束,因此自定义完成功能可以解决我的问题。有没有办法使vc使用一个完成函数,当它如上所述用作vim参数时,会同时在$ PATH中同时完成两个命令? 并同时将它们扩展到其完整路径?我知道如何编写基本的完成功能。无数小时的尝试(我选择不放在此处以保持混乱/减少发布时间)失败了,原因很简单,因为我不确定命令完成是否与同时全路径扩展b / c兼容,从而破坏了传统完成。
具有自定义完成功能,当我尝试找到位于“ vim〜/ bin / a / b / cmd2.sh”中的脚本“ cmd2.sh”但以“ c”开头时,会发生以下情况点击“”。
vim c<tab>
不是让我来完成这些选择
cmd1.sh cmd2.sh cmd3.sh
它会完成它在$ PATH中找到的第一个,并将其插入到可能为
的READLINE中。/ppp/n/m/cmd3.sh
当我真正想要的时候
~/bin/a/b/cmd2.sh
这有效地终止了完成查找,因为在READLINE中,我光标之前的词现在以/ppp/n/m/cmd3.sh开头,无法返回到cmd2.sh
我希望这很清楚。
谢谢
答案 0 :(得分:1)
这需要您的.bashrc
文件中有一些样板,但可能对您有用。它利用了目录栈(有人可能会说滥用目录栈,但是如果您不将其用于其他任何事情,那可能没问题)。
在您的.bashrc
中,将每个感兴趣的目录添加到目录堆栈中。以您的主目录结束列表,因为pushd
还会更改您的当前工作目录。
pushd ~/bin/x/y/cmd1.sh
pushd ~/bin/a/b/cmd2.sh
pushd /ppp/n/m/cmd3.sh
pushd ~
是的,它有点重复了您的PATH
条目,但是我认为您实际上不需要访问PATH
中的每个目录,只需访问您所在的目录有您要编辑的文件。 (您真的要尝试编辑/bin
或/usr/bin
中的任何内容吗?)
现在,在交互式外壳中,您可以运行dirs -v
来查看堆栈中的目录及其索引:
$ dirs -v
0 ~
1 /ppp/n/m
2 ~/bin/a/b
3 ~/bin/x/y
4 ~
现在,无论您身在何处,如果要编辑~/bin/x/y/cmd1.sh
,都可以使用
$ vi ~3/cmd3.sh
只要您不在其他地方使用popd
或pushd
来修改堆栈,索引将保持不变。 (使用pushd
将在堆栈顶部添加一个新目录,从而增加每个索引; popd
将在删除顶部目录后减少每个索引。)
一个简单得多的过程是简单地定义一些变量,这些变量的值是所需的目录:
binab=~/bin/a/b
binxy=~/bin/x/y
ppp=/ppp/n/m
然后简单地展开它们
$ vi $ppp/cmd3.sh
shell执行参数名称的完成,因此变量名称不必特别短,但是dirstack方法保证您只需要2或3个字符。 (而且,它不会用其他变量污染全局名称空间。)
答案 1 :(得分:0)
有趣的是,我发现自己想做类似的事情。我一起整理了以下bash脚本。这是不言自明的。如果要编辑我的一个脚本(例如,这个脚本是~/bin/vm
),则只需运行vm vm
。我可以在路径中打开多个文件,无论是在缓冲区中还是在垂直/水平拆分中……
随便使用它,然后将其粘贴到此处,因为它们都可以使用了:
#!/usr/bin/env bash
Usage() {
cat <<-__EOF_
${0##*/} Opens scripts in PATH from any location (vim -O)
Example: ${0##*/} ${0##*/}
opens this script in vim editor
-o: Change default behaviour (vim -O) to -o
-b: Change default behaviour to open in buffers (vim file1 file2)
-h: Display this message
__EOF_
}
flag="O"
vimopen() {
local wrapped
local located
local found
found=false
[ $# -lt 1 ] && echo "No script given" && return
wrapped=""
for arg in "$@"; do
if located=$(which "${arg}" 2> /dev/null); then
found=true
wrapped="${wrapped} ${located}"
else
echo "${arg} not found!"
fi
done
$found || return
# We WANT word splitting to occur here
# shellcheck disable=SC2086
case ${flag} in
O)
vim $wrapped -O
;;
o)
vim $wrapped -o
;;
*)
vim $wrapped
esac
}
while getopts :boh f; do
case $f in
h)
Usage
exit 0
;;
o)
flag="o"
shift
;;
b)
flag=""
shift
;;
*)
echo "Unknown option ${f}-${OPTARG}"
Usage
exit 1
;;
esac
done
vimopen "$@"