简体   繁体   English

如何为路径上的所有可执行文件编写bash完成脚本?

[英]How to write a bash completion script for all executables on path?

I've had this use case come up for a couple of different scripts I've written or modified. 我已经为这个用例提出了我编写或修改过的几个不同的脚本。 Essentially, I want bash completion for option '-x' to complete executables on the PATH. 基本上,我希望选项'-x'的bash完成以完成PATH上的可执行文件。 This is sort of two questions wrapped in one. 这是一个包含在一个问题中的两个问题。

So far I've had troubles because bash doesn't easily distinguish between aliases, builtins, functions, etc and executable files on the PATH. 到目前为止,我遇到了麻烦,因为bash不容易区分PATH上的别名,内置函数,函数等和可执行文件。 The _commands wrapper function in /usr/share/bash-completion/bash_completion completes on all of the above but I have no use for working with aliases, builtins, functions, etc and only want to complete on the commands that happen to be executables on the PATH. / usr / share / bash-completion / bash_completion中的_commands包装器函数完成了以上所有操作,但我没有使用别名,内置函数,函数等,只想完成碰巧可执行的命令路径。

So for example... If I enter scriptname -x bas[TAB] , it should complete with base64, bash, basename, bashbug. 例如......如果我输入scriptname -x bas[TAB] ,它应该用base64,bash,basename,bashbug完成。

This is what my completion script looks like now: 这就是我的完成脚本现在的样子:

_have pygsparkle && {

_pygsparkle(){
    local cur prev

    COMPREPLY=()
    cur=${COMP_WORDS[COMP_CWORD]}
    prev=${COMP_WORDS[COMP_CWORD-1]}

    case $prev in
    -x|--executable)
        # _command
        executables=$({ compgen -c; compgen -abkA function; } | sort | uniq -u)
        COMPREPLY=( $( compgen -W "$executables" -- "$cur" ) )
        return 0
        ;;
    esac

    if [[ $cur = -* ]]; then
        COMPREPLY=( $( compgen -W '--executable -h --help -x' -- "$cur" ) )
    fi

}

complete -F _pygsparkle pygsparkle

}

It seems to work as expected but { compgen -c; compgen -abkA function; } | sort | uniq -u 它似乎按预期工作但{ compgen -c; compgen -abkA function; } | sort | uniq -u { compgen -c; compgen -abkA function; } | sort | uniq -u { compgen -c; compgen -abkA function; } | sort | uniq -u is a pretty dirty hack. { compgen -c; compgen -abkA function; } | sort | uniq -u是一个非常脏的黑客。 In zsh you can get a sorted list of executables on PATH running print -rl -- ${(ko)commands} . 在zsh中,您可以在运行print -rl -- ${(ko)commands} PATH上获得可执行文件的排序列表。 So it appears I'm missing at least 60+ execs, likely because uniq -u is dumping execs with that same name as aliases or functions. 所以看来我至少缺少60多名高管,可能是因为uniq -u正在转储与别名或函数同名的高管。

Is there a better way to do this? 有一个更好的方法吗? Either a better command for getting all executables on PATH or a pre-existing completion function that will serve the same ends? 获取PATH上所有可执行文件的更好命令,或者服务于相同目的的预先存在的完成函数?

Update: Ok so the following function executes in under 1/6 sec and looks like the best option. 更新:确定以下功能在1/6秒内执行,看起来是最好的选择。 Unless there are any other suggestions I'll probably just close the question. 除非有任何其他建议,我可能只是关闭这个问题。

_executables(){
    while read -d $'\0' path; do
        echo "${path##*/}"
    done < <(echo -n "$PATH" | xargs -d: -n1 -I% -- find -L '%' -maxdepth 1 -mindepth 1 -type f -executable -print0 2>/dev/null) | sort -u
}

I have a run-in-background script . 我有一个run-in-back脚本 To get autocomplete for it I use bash's builtin complete : 为了获得它的自动完成,我使用bash的内置complete

> complete -c <script-name>
> <script-name> ech[TAB]
> <script-name> echo 

From the docs : 来自文档

 command Command names. May also be specified as -c. 

To deal with non-executables, my first thought was to filter with which or type -P but that's terribly slow. 为了处理非可执行文件,我的第一个想法是过滤whichtype -P但这非常慢。 A better way is to only check the unknowns. 更好的方法是只检查未知数。 Use @Six's solution or some other to find items in compgen -c but not compgen -abkA function . 使用@ Six的解决方案或其他一些解决方案来查找compgen -c项目,而不是compgen -abkA function For everything in both lists, check with type -P . 对于两个列表中的所有内容,请检查type -P For example: 例如:

function _executables {
    local exclude=$(compgen -abkA function | sort)
    local executables=$(
        comm -23 <(compgen -c) <(echo $exclude)
        type -tP $( comm -12 <(compgen -c) <(echo $exclude) )
    )
    COMPREPLY=( $(compgen -W "$executables" -- ${COMP_WORDS[COMP_CWORD]}) )
}
complete -F _executables <script-name>

I've tried a few times but introducing -F to complete just seems to be a slow way to do thing. 我已经尝试了几次,但介绍-F complete似乎是一种缓慢的做事方式。 Not to mention now having to handle absolute/relative path completion to executables, the horribly fiddly complete-the-directory-but-don't-add-a-space bit and correctly handling spaces in paths. 更不用说现在必须处理可执行文件的绝对/相对路径完成,非常繁琐的完整目录但不添加空间位并正确处理路径中的空间。

看起来你正在寻找compgen -c

There doesn't seem to be a simple answer to the question of how to list all the executables available on a user's PATH. 对于如何列出用户PATH上可用的所有可执行文件的问题,似乎没有一个简单的答案。 Alas, I have searched far and wide for an answer. 唉,我已经广泛搜索了一个答案。

compgen may at first seem like the right direction but it lacks an option to show only executables compgen最初看起来可能是正确的方向,但它缺少只显示可执行文件的选项

compgen -c will show all commands (this includes aliases, builtins, executables, functions, and keywords) compgen -c将显示所有命令(包括别名,内置函数,可执行文件,函数和关键字)

compgen -abkA function will show all commands except executables compgen -abkA function将显示可执行文件之外的所有命令

So an approximation of the executables available can be surmised by 'diffing' the two, ie { compgen -c; compgen -abkA function; } | sort | uniq -u 因此可以通过“区分”两者来推测可用的可执行文件的近似值,即{ compgen -c; compgen -abkA function; } | sort | uniq -u { compgen -c; compgen -abkA function; } | sort | uniq -u { compgen -c; compgen -abkA function; } | sort | uniq -u , but that clearly has some issues. { compgen -c; compgen -abkA function; } | sort | uniq -u ,但这显然有一些问题。

NOTE: To confirm that compgen -c will not work, all you have to do is scan through the results and identify many non-executable entries. 注意:要确认compgen -c不起作用,您所要做的就是扫描结果并识别许多不可执行的条目。 You can also try diff -u <(compgen -A function -abck | sort -u) <(compgen -c | sort -u) and see that the commands are equivalent (once duplicates have been removed of course). 您也可以尝试diff -u <(compgen -A function -abck | sort -u) <(compgen -c | sort -u) ,看看命令是否相同(当然一旦删除了重复项)。

So it seems the best option is to scan through every directory on the path. 因此,最好的选择是扫描路径上的每个目录。

In summary, the following is the best option on a modern system. 总之,以下是现代系统的最佳选择。 It has a speedy runtime (~0.05 seconds) comparable to compgen -c and suitable for completions. 它具有与compgen -c相当的快速运行时间(~0.05秒),适用于完成。

executables(){
    echo -n "$PATH" | xargs -d: -I{} -r -- find -L {} -maxdepth 1 -mindepth 1 -type f -executable -printf '%P\n' 2>/dev/null | sort -u
}

If you are using ancient versions of find/xargs (ie busybox or BSD) and/or using mksh (Android's default shell) which doesn't support process substitution, you will want the following. 如果您使用的是旧版本的find / xargs(即busybox或BSD)和/或使用不支持进程替换的mksh(Android的默认shell),您将需要以下内容。 Not so speedy (~3.5 seconds). 不那么快(~3.5秒)。

executables(){
    find ${PATH//:/ } -follow -maxdepth 1 -type f -exec test -x '{}' \; -print0 2>/dev/null \
    | while read -d $'\0' path; do
        echo "${path##*/}"
    done \
    | sort -u
}

If you are using the aforementioned setup and have spaces in your PATH for some reason, then this should work. 如果您使用上述设置并且由于某种原因在PATH中有空格,那么这应该可行。

executables(){
    echo "$PATH" \
    | tr ':' '\n' \
    | while IFS= read path; do
        find "$path" -follow -maxdepth 1 -type f -exec test -x '{}' \; -print0 2>/dev/null
    done \
    | while read -d $'\0' path; do
        echo "${path##*/}"
    done \
    | sort -u
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM