Как написать сценарий завершения bash для всех исполняемых файлов на пути?

У меня был этот вариант использования для пары разных сценариев, которые я написал или модифицировал. По сути, я хочу, чтобы завершение bash для опции '-x' завершало выполнение исполняемых файлов в PATH. Это своего рода два вопроса, объединенные в один.

До сих пор у меня были проблемы, потому что bash не различает псевдонимы, встроенные функции, функции и т. Д. И исполняемые файлы в PATH. Функция-оболочка _commands в / usr / share / bash-Завершение / bash_completion завершает все вышеперечисленное, но я не использую для работы с псевдонимами, встроенными функциями, функциями и т. Д. И хочу завершить только те команды, которые оказались исполняемыми на путь.

Так, например ... Если я введу scriptname -x bas[TAB], он должен завершиться base64, bash, basename, bashbug.

Вот как сейчас выглядит мой сценарий завершения:

_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

}

Кажется, это работает, как и ожидалось, но { compgen -c; compgen -abkA function; } | sort | uniq -u - довольно грязный хакер. В zsh вы можете получить отсортированный список исполняемых файлов по PATH с print -rl -- ${(ko)commands}. Похоже, мне не хватает как минимум 60+ исполнителей, вероятно, потому, что uniq -u выгружает исполнителей с тем же именем, что и псевдонимы или функции.

Есть лучший способ сделать это? Либо лучшая команда для получения всех исполняемых файлов в PATH, либо уже существующая функция завершения, которая будет служить тем же целям?

Обновление. Итак, следующая функция выполняется менее чем за 1/6 секунды и выглядит наилучшим вариантом. Если не будет других предложений, я, наверное, просто закрою вопрос.

_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
}

person Six    schedule 03.04.2015    source источник
comment
Команда sed будет работать, если вы сделаете : необязательным в конце: sed -r 's/([^:]+):*/find "\1" -maxdepth 1 -type f -executable -exec basename "{}" \\;\n/eg' <<< $PATH | sort | uniq ... Однако ваша попытка bash выглядела хорошо. Что не так с этим? Представление?   -  person hek2mgl    schedule 03.04.2015
comment
Выполнение такой обширной команды find для завершения на практике, похоже, не сработало, так как терминал зависал на несколько секунд для каждого [TAB]. Просто надеюсь, что уже есть функция завершения или возможность сделать это лучше, иначе мне, возможно, придется отказаться от нее.   -  person Six    schedule 03.04.2015
comment
Я действительно не понимаю, что не так с compgen -A function -abck?   -  person hek2mgl    schedule 03.04.2015
comment
compgen -A function -abck включает всевозможные команды (псевдонимы, встроенные функции, функции, ключевые слова, исполняемые файлы в PATH). Мне просто нужны исполняемые файлы по пути. Например, один из скриптов - это оболочка для пигментации. Я хочу иметь возможность легко выделять синтаксис в установленных скриптах. Завершение его до псевдонимов или ключевых слов было бы контрпродуктивным. В любом случае, это всего лишь один пример, но он у меня возникал в ряде ситуаций.   -  person Six    schedule 03.04.2015
comment
Тогда используйте только compgen -c ..   -  person hek2mgl    schedule 03.04.2015
comment
Да, насколько я знаю, compgen -c то же самое, что и compgen -A function -abck. Я не думаю, что есть опция compgen для перечисления только исполняемых файлов, но было бы очень хорошо, если бы она была.   -  person Six    schedule 03.04.2015
comment
Проверьте это: stackoverflow.com/questions/948008/ .. Также попробуйте diff -u <(compgen -A function -abck) <(compgen -c)   -  person hek2mgl    schedule 03.04.2015
comment
Они только кажутся разными, потому что compgen -A function -abck показывает кучу дубликатов. Попробуйте вот так, чтобы убрать все дубли: diff -u <(compgen -A function -abck | sort -u) <(compgen -c | sort -u). Ответьте на stackoverflow.com/questions/948008/, я думаю, немного вводил в заблуждение, потому что он должен быть compgen -c для всего вышеперечисленного ... Верно?   -  person Six    schedule 03.04.2015


Ответы (3)


У меня есть сценарий запуска в фоновом режиме. Чтобы получить автозаполнение, я использую встроенную команду bash complete:

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

Из документов:

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

Чтобы иметь дело с неисполняемыми файлами, моей первой мыслью было отфильтровать с помощью which или type -P, но это ужасно медленно. Лучше всего проверять только неизвестное. Используйте решение @ Six или другое, чтобы найти элементы в compgen -c, но не в compgen -abkA function. Для всего в обоих списках проверьте с помощью type -P. Например:

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>

Я пробовал несколько раз, но введение -F в complete кажется медленным путем. Не говоря уже о необходимости обрабатывать абсолютное / относительное завершение пути к исполняемым файлам, ужасно неудобный бит complete-the-directory-but-don't-add-a-space и правильная обработка пробелов в путях.

person jozxyqk    schedule 06.02.2017
comment
complete -c дает то же завершение, что и compgen -c, которое включает все вышеупомянутые нежелательные элементы, такие как псевдонимы, встроенные функции, функции и ключевые слова. Чтобы дать вам реальный пример, буквально на прошлой неделе я писал несколько сценариев оболочки, один для включения режима презентации XFCE (который запрещает заставку и приостановку) при выполнении некоторых команд, а другой - только для запрета приостановки. Если бы я использовал complete -c presentation-wrapper, я бы получил кучу нежелательных завершений, таких как command, declare, done и while. Я полагаю, они также проблематичны для вашего сценария. - person Six; 07.02.2017
comment
@Six Ааа, спасибо, что проявили ко мне терпение! Я попробовал, но все еще есть приличная задержка в 0,1-0,2 секунды и множество крайних случаев, которые нужно обработать. - person jozxyqk; 10.02.2017

Похоже, вы ищете compgen -c

person hek2mgl    schedule 03.04.2015
comment
Я попробовал указанную выше команду, и на самом деле у меня возникают некоторые ошибки разрешений, которых не должно быть. Выполнение этой команды sed -r 's/([^:]+):/find "\1" -maxdepth 1 -type f -executable -exec basename {} \\;\n/eg' <<< $PATH | sort | uniq | wc -l приводит к 2929. Другая команда, которую я решил использовать, - это find ${PATH//:/ } -maxdepth 1 -executable -exec basename '{}' \; 2>/dev/null | sort -u | wc -l, и я получаю 3701, что является правильным числом. Обе эти команды занимают около 3-4 секунд, так что это не очень хорошо, но, по крайней мере, кажется точным. - person Six; 03.04.2015
comment
Хм, я вижу, на моей машине то же самое. (Кстати, ваша команда bash намного лучше, чем мой подход sed! Похоже, я слишком много играю с sed) - person hek2mgl; 03.04.2015

Кажется, нет простого ответа на вопрос о том, как перечислить все исполняемые файлы, доступные в пользовательском PATH. Увы, я искал ответ повсюду.

compgen на первый взгляд может показаться правильным, но в нем отсутствует возможность отображать только исполняемые файлы.

compgen -c покажет все команды (включая псевдонимы, встроенные команды, исполняемые файлы, функции и ключевые слова)

compgen -abkA function покажет все команды кроме исполняемых файлов

Таким образом, можно предположить приблизительное количество доступных исполняемых файлов, "различая" два, то есть { compgen -c; compgen -abkA function; } | sort | uniq -u, но это явно имеет некоторые проблемы.

ПРИМЕЧАНИЕ. Чтобы убедиться, что compgen -c не будет работать, все, что вам нужно сделать, это просмотреть результаты и определить множество неисполняемых записей. Вы также можете попробовать diff -u <(compgen -A function -abck | sort -u) <(compgen -c | sort -u) и убедиться, что команды эквивалентны (разумеется, после удаления дубликатов).

Таким образом, кажется, что лучший вариант - просмотреть все каталоги на пути.

Таким образом, следующий вариант является лучшим вариантом для современной системы. Он имеет быстрое время выполнения (~ 0,05 секунды), сравнимое с compgen -c, и подходит для доработок.

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
}

Если вы используете старые версии find / xargs (например, busybox или BSD) и / или используете mksh (оболочку по умолчанию для Android), которая не поддерживает подстановку процессов, вам понадобится следующее. Не так быстро (~ 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
}

Если вы используете вышеупомянутую настройку и по какой-то причине в вашем 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
}
person Six    schedule 09.04.2015