Bash 完成脚本 - 获得类似 "transparent proxy" 的行为
Bash completion scripting - getting a "transparent proxy"-like behaviour
我正在尝试为 运行 将其参数作为命令的程序编写一个简单的 Bash 完成脚本。一个很好的例子是 nvidia-prime
包提供的 prime-run
脚本:
#!/bin/bash
__NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only __GLX_VENDOR_LIBRARY_NAME=nvidia "$@"
这个脚本设置了一些环境变量,指示主要驱动程序在混合系统上使用 Nvidia dGPU。第一个参数被视为命令,所有尾随参数都被传递。因此,例如您可以 运行 prime-run code .
并且 VSCode 将使用 dGPU 在当前目录中启动。
因此,从完成脚本 POV 来看,我们想要的是基本上尝试完成,就好像 prime-run
令牌不存在一样(因此“透明代理”行为)。举一个相当人为的例子:
> prime-run journalc<TAB>
(completes journalctl)
> prime-run journalctl --us<TAB>
(completes --user)
但是我发现这在 Bash 中非常困难(我不知道在其他 shell 中如何)。所以问题很简单:是否可能,如果可能如何?
我(绝望地)有过的想法
- 简单的
complete -A command prime-run
:第一个参数按预期作为命令完成(我们称它为foo
),但以下参数也作为命令而不是[=]的参数完成19=]
- 使用
compgen
和 complete -p
的某种组合来调用 foo
的完成函数,但据我所知,所有 foo
的完成函数都是本地定义的,因此无法调用
TL;DR
bash-completion
提供了一个名为_command_offset
(permalink)的函数,正是我需要的
# A meta-command completion function for commands like sudo(8), which need to
# first complete on a command, then complete according to that command's own
# completion definition.
如果您对我如何来到这里感兴趣,请继续阅读。
所以前几天我在做白日梦,当它击中我时 - sudo
基本上没有我想要的完全相同的行为吗?因此任务变得简单 - 对 sudo
的完成脚本进行逆向工程。此处提供来源:permalink.
事实证明,大部分代码都与完成各种选项有关,因此简单地将大部分代码扔掉是安全的:
- L 8-11、50-52:与
sudo
的编辑模式有关。可以安全地开沟。
- L 19-24、27-39、43-49:这些完成了
sudo
的选项。可以安全地开沟。
所以我们剩下这个:
_sudo()
{
local cur prev words cword split
_init_completion -s || return
for ((i = 1; i <= cword; i++)); do
if [[ ${words[i]} != -* ]]; then
local PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin
local root_command=${words[i]}
_command_offset $i
return
fi
done
$split && return
} &&
complete -F _sudo sudo sudoedit
-
for
和 if
块用于处理“来宾命令”之前的 sudo
选项。可以安全地放弃(在将所有 $i
替换为 1
之后)。
- 变量
$split
只在_init_completion
(permalink)中被引用,似乎是用来处理不同的参数样式(--foo=bar
v.s.--foo bar
).与 -s
标志相同。无关紧要。
- 附加到
$PATH
和设置$root_command
与权限升级有关。仅与 sudo
. 有关
所以在尘埃落定之后,通过排除过程,我得到了这个简单的代码块:
_my-script()
{
local cur prev words cword
_init_completion || return
_command_offset 1
} && complete -F _my-script my-script
声明这四个局部变量并调用 _init_completion
是所有完成脚本的标准,所以实际上它就像一个命令一样简单。当然有人必须编写 massively-complex _command_offset
函数,我猜我很幸运吧?
无论如何,感谢你阅读我的胡闹故事,希望这对以后的其他人有所帮助。
我正在尝试为 运行 将其参数作为命令的程序编写一个简单的 Bash 完成脚本。一个很好的例子是 nvidia-prime
包提供的 prime-run
脚本:
#!/bin/bash
__NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only __GLX_VENDOR_LIBRARY_NAME=nvidia "$@"
这个脚本设置了一些环境变量,指示主要驱动程序在混合系统上使用 Nvidia dGPU。第一个参数被视为命令,所有尾随参数都被传递。因此,例如您可以 运行 prime-run code .
并且 VSCode 将使用 dGPU 在当前目录中启动。
因此,从完成脚本 POV 来看,我们想要的是基本上尝试完成,就好像 prime-run
令牌不存在一样(因此“透明代理”行为)。举一个相当人为的例子:
> prime-run journalc<TAB>
(completes journalctl)
> prime-run journalctl --us<TAB>
(completes --user)
但是我发现这在 Bash 中非常困难(我不知道在其他 shell 中如何)。所以问题很简单:是否可能,如果可能如何?
我(绝望地)有过的想法
- 简单的
complete -A command prime-run
:第一个参数按预期作为命令完成(我们称它为foo
),但以下参数也作为命令而不是[=]的参数完成19=] - 使用
compgen
和complete -p
的某种组合来调用foo
的完成函数,但据我所知,所有foo
的完成函数都是本地定义的,因此无法调用
TL;DR
bash-completion
提供了一个名为_command_offset
(permalink)的函数,正是我需要的
# A meta-command completion function for commands like sudo(8), which need to
# first complete on a command, then complete according to that command's own
# completion definition.
如果您对我如何来到这里感兴趣,请继续阅读。
所以前几天我在做白日梦,当它击中我时 - sudo
基本上没有我想要的完全相同的行为吗?因此任务变得简单 - 对 sudo
的完成脚本进行逆向工程。此处提供来源:permalink.
事实证明,大部分代码都与完成各种选项有关,因此简单地将大部分代码扔掉是安全的:
- L 8-11、50-52:与
sudo
的编辑模式有关。可以安全地开沟。 - L 19-24、27-39、43-49:这些完成了
sudo
的选项。可以安全地开沟。
所以我们剩下这个:
_sudo()
{
local cur prev words cword split
_init_completion -s || return
for ((i = 1; i <= cword; i++)); do
if [[ ${words[i]} != -* ]]; then
local PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin
local root_command=${words[i]}
_command_offset $i
return
fi
done
$split && return
} &&
complete -F _sudo sudo sudoedit
-
for
和if
块用于处理“来宾命令”之前的sudo
选项。可以安全地放弃(在将所有$i
替换为1
之后)。 - 变量
$split
只在_init_completion
(permalink)中被引用,似乎是用来处理不同的参数样式(--foo=bar
v.s.--foo bar
).与-s
标志相同。无关紧要。 - 附加到
$PATH
和设置$root_command
与权限升级有关。仅与sudo
. 有关
所以在尘埃落定之后,通过排除过程,我得到了这个简单的代码块:
_my-script()
{
local cur prev words cword
_init_completion || return
_command_offset 1
} && complete -F _my-script my-script
声明这四个局部变量并调用 _init_completion
是所有完成脚本的标准,所以实际上它就像一个命令一样简单。当然有人必须编写 massively-complex _command_offset
函数,我猜我很幸运吧?
无论如何,感谢你阅读我的胡闹故事,希望这对以后的其他人有所帮助。