Есть ли способ автоматического завершения команды открытия в Терминале?

16

Я часто использую open -a команду в Терминале для открытия приложений через ssh. Как мне сделать так, чтобы приложение автоматически заполняло имя приложения?

daviesgeek
источник
Какую оболочку вы используете?
Даниэль Бек
2
Я предполагаю, что немного более быстрый способ - это набрать полный путь (просто пойдем со мной на некоторое время здесь !!), например open -a /Applications/Textedit.app foo.txt(я полагаю, это то, что вы пытаетесь сделать). Если нажать Tab после /Aиз , /Applicationsа затем Tab снова после того , как /Teв /Textedit.appто , что должно автозаполнения обе части для вас , как вы идете. Не идеал, я признаю, но, возможно, но лучше. Это было использование Bash.
двоичный боб
Вы также можете попробовать следовать инструкциям в конце этого поста: brettterpstra.com/grabbing-a-mac-apps-icon-advanced-bash-usage-2
Lri
@DanielBeck Bash.
daviesgeek

Ответы:

9
_complete_open() {
        COMPREPLY=()
        local cur="${COMP_WORDS[$COMP_CWORD]}"
        local prev="${COMP_WORDS[COMP_CWORD-1]}"
        [[ "$cur" == -* || "$prev" != '-a' ]] && return
        apps="$(mdfind kMDItemKind==Application -onlyin /Applications -onlyin ~/Applications -onlyin /Developer -onlyin ~/Developer | grep -v '/.*/.*/.*/.*/' | sed -E 's|.*/||g;s|\.app$||g' | uniq)"$'Finder\nArchive Utility\nCharacterPalette\nKeyboardViewer'
        local IFS=$'\n'
        if [[ "${cur:0:1}" = '"' || "${cur:0:1}" = "'" ]]; then
            quote="${cur:0:1}"
            cur="${cur:1}"
        fi
        local found="$(grep -i "^$cur" <<< "$apps")"
        if [[ "$quote" == '"' ]]; then
            found="$(sed "s|^|\"|g;s|$|\"|g" <<< "$found")"
        elif [[ "$quote" == "'" ]]; then
            found="$(sed "s|^|'|g;s|$|'|g" <<< "$found")"
        else
            found="$(sed 's| |\\ |g' <<< "$found")"
        fi
        COMPREPLY=($found)
}

complete -o default -F _complete_open open

Третья версия, которая теперь должна учитывать регистр и работать в кавычках.

LRI
источник
Я не мог заставить это работать DVD Player. Есть идеи что не так? Выглядит как табуляция вместо пробела ...
Даниэль Бек
@DanielBeck Это отсутствовало IFS=$'\n'. Во всяком случае, я отредактировал ответ снова.
Lri
Выглядит хорошо. Позднее +1 за mdfindидею. Хорошая работа по оптимизации и исправлениям. CoreServicesвероятно, личные предпочтения, хотя. В чем причина, nospaceхотя? Если есть только 1 программа, я хочу продолжить прямо сейчас с открытым полем. Просто личные предпочтения? Есть идеи по поводу оставшейся проблемы с цитированием? AFAICT, это единственное, что осталось для правильного решения.
Даниэль Бек
@DanielBeck Я всегда ставлю -aфлаг в конце, так что, думаю, -o nospaceэто всего лишь личное предпочтение. Может быть, нам следует пинговать Бретта Терпстра или что-то еще, чтобы закончить этот нердфест ...
Lri
Огромное спасибо!! @DanielBeck ты тоже. Я выбрал версию Лри из-за скорости. К сожалению, ваш был немного медленным. Большое спасибо вам обоим! Вы мне очень помогли и увеличили скорость моего терминала.
daviesgeek
7

Добавьте следующее в ваш .bash_profileили .bashrcи запустите новый сеанс:

function _complete_open {
    cur=$2
    COMPREPLY=( );

    [[ "$COMP_WORDS" = "open" ]] || return
    [[ "${COMP_WORDS[ $(( $COMP_CWORD - 1 )) ]}" = "-a" ]] || return

    OLDIFS="$IFS"
    IFS=$'\n'
    local _part="${COMP_WORDS[$COMP_CWORD]}"

    if [[ "${_part:0:1}" = '"' || "${_part:0:1}" = "'" ]] ; then
        COMPREPLY=( $( compgen -W "$( mdfind kMDItemKind==Application | sed -e 's|.*/||g' -e 's|.app$||' | sort -u )" -- $cur ) )
    else
        COMPREPLY=( $( compgen -W "$( mdfind kMDItemKind==Application | sed -e 's|.*/||g' -e 's|.app$||' -e 's| |\\\\ |g' | sort -u )" -- $cur ) )
    fi
    IFS="$OLDIFS"
}

complete -o default -F _complete_open open

Не нужно ничего устанавливать. Это работает с bashиз коробки.


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

Получены результаты system_profiler SPApplicationsDataType, что является самым простым способом получить все приложения, которые могут быть запущены таким образом в вашей системе. Список обрабатывается так, чтобы возвращать только имена программ, которые могут содержать пробелы и могут отличаться от имен пакетов (даже если игнорируется .appсуффикс)

Использование: введите open -a, затем пробел, затем нажмите Tabили Esc(дважды в моей системе, не уверен, что это везде).

Пример, показывающий все вспомогательные приложения для моего сканера:

$ open -a Scan
Scan to E-mail          Scan to Excel           Scan to Folder          Scan to Print           Scan to Searchable PDF  Scan to Word            ScanSnap Manager

Недостатки и проблемы этого решения:

  • В вашей системе есть тонны программ, о которых вы, возможно, и не подозреваете, как и все остальное /System/Library/CoreServices. Возможно, вы не захотите перечислить все из них. ОТО, это действительно легко увидеть и запустить, например, CharacterPaletteили KeyboardViewerтак. * Настройте mdfindвызов (ы) соответствующим образом с -onlyinаргументом.

  • Это вроде медленно, из-за system_profiler SPApplicationsDataType. Возможно, вам придется подождать секунду или две, прежде чем завершится обнаружение. Теперь использует mdfindдля быстрого получения программ. Спасибо @Lri

  • Он может обрабатывать пробелы в именах приложений и заключать в кавычки имена программ, но это довольно странно. Требуется, чтобы в качестве первого символа использовалась кавычка: пока Scan" to "Pона действительна bash, эта программа не обнаружит ее. Завершение также не работает после пробела (например Scan\ to), используйте кавычки в таких случаях ( "Scan to). Поддержка сбежавших пространств только хорошо завершить DVDк DVD\ Player.

Даниэль Бек
источник
Не mdfind 'kMDItemKind==Application'будет быстрее? Если completion-ignore-caseустановлено, grep, вероятно, также должен игнорировать регистр.
Lri
@Lri Ты прав. Не удалось найти случай, когда эти результаты имели бы значение.
Даниэль Бек
3

Программируемое автозаполнение на помощь! Тем не менее, нужно было много копировать с домашней страницы Bash Completion , которую в любом случае стоит установить для большого количества магии автозаполнения. Если вы это сделаете, вам понадобятся только последняя функция ( _open) и команда инициализации снизу.

Добавьте следующее к .bashrc:

# taken from http://bash-completion.alioth.debian.org/

_compopt_o_filenames()
{
    # We test for compopt availability first because directly invoking it on
    # bash < 4 at this point may cause terminal echo to be turned off for some
    # reason, see https://bugzilla.redhat.com/653669 for more info.
    type compopt &>/dev/null && compopt -o filenames 2>/dev/null || \
        compgen -f /non-existing-dir/ >/dev/null
}

_tilde() {
    local result=0
    # Does $1 start with tilde (~) and doesn't contain slash (/)?
    if [[ ${1:0:1} == "~" && $1 == ${1//\/} ]]; then
        _compopt_o_filenames
        # Try generate username completions
        COMPREPLY=( $( compgen -P '~' -u "${1#\~}" ) )
        result=${#COMPREPLY[@]}
    fi
    return $result
}

_quote_readline_by_ref()
{
    if [[ ${1:0:1} == "'" ]]; then
        if [[ ${BASH_VERSINFO[0]} -ge 4 ]]; then
            # Leave out first character
            printf -v $2 %s "${1:1}"
        else
            # Quote word, leaving out first character
            printf -v $2 %q "${1:1}"
            # Double-quote word (bash-3)
            printf -v $2 %q ${!2}
        fi
    elif [[ ${BASH_VERSINFO[0]} -le 3 && ${1:0:1} == '"' ]]; then
        printf -v $2 %q "${1:1}"
    else
        printf -v $2 %q "$1"
    fi

    # If result becomes quoted like this: $'string', re-evaluate in order to
    # drop the additional quoting.  See also: http://www.mail-archive.com/
    # bash-completion-devel@lists.alioth.debian.org/msg01942.html
    [[ ${!2:0:1} == '$' ]] && eval $2=${!2}
} # _quote_readline_by_ref()

_filedir()
{
    local i IFS=$'\n' xspec

    _tilde "$cur" || return 0

    local -a toks
    local quoted tmp

    _quote_readline_by_ref "$cur" quoted
    toks=( ${toks[@]-} $(
        compgen -d -- "$quoted" | {
            while read -r tmp; do
                printf '%s\n' $tmp
            done
        }
    ))

    if [[ "$1" != -d ]]; then
        # Munge xspec to contain uppercase version too
        [[ ${BASH_VERSINFO[0]} -ge 4 ]] && \
            xspec=${1:+"!*.@($1|${1^^})"} || \
            xspec=${1:+"!*.@($1|$(printf %s $1 | tr '[:lower:]' '[:upper:]'))"}
        toks=( ${toks[@]-} $( compgen -f -X "$xspec" -- $quoted) )
    fi
    [ ${#toks[@]} -ne 0 ] && _compopt_o_filenames

    COMPREPLY=( "${COMPREPLY[@]}" "${toks[@]}" )
} # _filedir()

# only the following is needed if bash-autocompletion is already installed
_open ()
{
    local cur;

    cur=$2;

    COMPREPLY=();
    if [ $COMP_CWORD -eq 2 ]; then
        COMPREPLY=($(compgen -W "$(/bin/ls /Applications)" -- $cur ));
        return 0
    fi

    _filedir
}

complete -F _open open
Nohillside
источник
Поэтому я следовал инструкциям по установке и добавил код .bashrc. Теперь, как мне получить автозаполнение? Что я нажимаю для автозаполнения? (Извините, я не могу прочитать код)
daviesgeek
1
Не обрабатывает имена программ с пробелами (например DVD Player.app). Также перечисляет каталоги /Applications(например iWork 09, отдельные записи iWorkи 09), но не программы, содержащиеся в них.
Даниэль Бек
1

Это включается по умолчанию в оболочке Zsh , если, конечно, вы включили завершение. Zsh включен в OS X.

Все, что вам нужно сделать, это поместить autoload -Uz compinit; compinitв ~ / .zshrc или включить завершение через меню конфигурации первого запуска.

Zsh - это в основном надмножество Bash, поэтому вам не нужно изучать что-то новое. Даже ваши сценарии, вероятно, будут работать!

Серебряный Волк - Восстановить Монику
источник