Завершение Bash, чтобы команда «cd» заполнила рабочие каталоги из других запущенных оболочек?

Я пытаюсь написать завершение bash, которое позволит мне заполнить имена каталогов, в которых находятся другие оболочки.

Например, предположим, что у меня есть другая оболочка, открытая в /very/long/path/name, и я сейчас нахожусь в каталоге, содержащем подкаталоги foo и bar. Когда я набираю cd <Tab>, я хочу видеть:

$ cd <Tab>
foo/  bar/  /very/long/path/name

У меня есть эта команда для создания списка возможных завершений:

ps -Cbash -opid= | xargs pwdx | cut -d" " -f2 | sort -u | while read; do echo ${REPLY#$PWD/}; done | grep -v "^$"

Для краткости я напишу это как ...pipeline....

В моей системе есть функция _cd, которая выполняет обычное завершение:

$ complete -p cd
complete -o nospace -F _cd cd

Я хотел бы повторно использовать эту функцию _cd, потому что она нетривиальна (~30 строк кода, согласно type _cd). Бонусные баллы, если решение повторно использует уже определенное завершение, независимо от того, основано оно на функции с именем _cd.

Я думал, что вариант -C для complete звучит многообещающе, но я не могу заставить его работать:

$ complete -C '...pipeline...' cd
$ cd <Tab>grep: cd: No such file or directory
grep: : No such file or directory
grep: cd: No such file or directory

Написание моей собственной функции-оболочки для -F, которая добавляется к массиву COMPREPLY, также не совсем сработало:

$ function _cd2() { _cd; COMPREPLY=( ${COMPREPLY[@]} $(...pipeline...) ); }
$ cd <Tab>
foo/  bar/  name/

Он удаляет все компоненты пути, кроме последнего. Я полагаю, это должно быть что-то, установленное _cd, но я не знаю, как этому противодействовать.

Если я уберу вызов _cd из _cd2, я увижу завершения, но они не будут правильно завершать частичные имена каталогов. Если я наберу cd /ve<Tab>, он все равно покажет полный путь без фактического завершения моей командной строки.

Как я могу заставить это делать то, что я хочу?


Приложение: полное определение _cd:

$ type _cd
_cd is a function
_cd () 
{ 
    local cur prev words cword;
    _init_completion || return;
    local IFS='
' i j k;
    compopt -o filenames;
    if [[ -z "${CDPATH:-}" || "$cur" == ?(.)?(.)/* ]]; then
        _filedir -d;
        return 0;
    fi;
    local -r mark_dirs=$(_rl_enabled mark-directories && echo y);
    local -r mark_symdirs=$(_rl_enabled mark-symlinked-directories && echo y);
    for i in ${CDPATH//:/'
'};
    do
        k="${#COMPREPLY[@]}";
        for j in $( compgen -d $i/$cur );
        do
            if [[ ( -n $mark_symdirs && -h $j || -n $mark_dirs && ! -h $j ) && ! -d ${j#$i/} ]]; then
                j+="/";
            fi;
            COMPREPLY[k++]=${j#$i/};
        done;
    done;
    _filedir -d;
    if [[ ${#COMPREPLY[@]} -eq 1 ]]; then
        i=${COMPREPLY[0]};
        if [[ "$i" == "$cur" && $i != "*/" ]]; then
            COMPREPLY[0]="${i}/";
        fi;
    fi;
    return 0
}

person Thomas    schedule 24.10.2014    source источник
comment
Похоже, ваш пользовательский _cd вызывает _init_completion, _filedir и, возможно, некоторые другие функции: каковы определения этих функций?   -  person bishop    schedule 24.10.2014
comment
Уф, каждая из них вызывает несколько других функций... Я работаю в системе Ubuntu, если это поможет, поэтому, если у вас есть доступ к одной из них, вы сможете копнуть глубже.   -  person Thomas    schedule 24.10.2014
comment
К сожалению, для меня нет Ubuntu. CentOS, Gentoo, Arch, Slack...   -  person bishop    schedule 24.10.2014


Ответы (1)


Вам нужно оценить текущее соответствие по списку комбинированных вариантов. Вот тестовый сценарий, который иллюстрирует движущиеся части:

#!/bin/bash

mkdir -p {my,other}/path/to/{a,b,c}

function _cd() {
    COMPREPLY=( my/path/to/a my/path/to/b );
}
complete -o nospace -F _cd cd

function _cd2() {
    local cur opts;
    cur="${COMP_WORDS[COMP_CWORD]}";
    _cd;
    opts="${COMPREPLY[@]} other/path/to/c";        # here we combine options
    COMPREPLY=($(compgen -W "${opts}" -- ${cur})); # here is the secret sauce
}
complete -F _cd2 cd

complete -p cd

Наиболее важные точки находятся в compgen части _cd2: здесь выбираются наиболее подходящие варианты из комбинированного набора вариантов (в $opts).

person bishop    schedule 24.10.2014
comment
Хах, еще какая такая тривиальная _cd функция? Я не ожидал этого. Действительно интересно... - person Thomas; 24.10.2014
comment
@ Томас: Ага. Думаю, я понял это. Обновите и проверьте мои изменения. - person bishop; 24.10.2014
comment
Это здорово, это почти работает! Однако, если я заменю настоящий _cd, он больше не покажет мои хорошие пути, а только стандартные. В вопрос добавлено определение _cd. - person Thomas; 24.10.2014
comment
Ах, compopt +o filenames отменяет этот эффект. Но теперь я столкнулся со стеной: для /v, /very/long/path/name и /very оба являются допустимыми завершениями, поэтому он завершает только /very, нарушая цель. Нужно будет больше подумать о том, как я хочу, чтобы это работало... - person Thomas; 24.10.2014
comment
@ Томас, я бы подумал об использовании // для ваших префиксов, основанных на использовании, и / в противном случае - чтобы обеспечить чистый способ различения. Конечно, compopt filenames все еще может попытаться завершить первое, но это дает вам четкое место для начала в различении намерений пользователя. - person Charles Duffy; 24.10.2014
comment
Это хорошая идея. В итоге я просто написал function ocd() { cd "@$"; } и использовал его вместо этого. Хорошо, что теперь у меня есть команда под названием ocd :) - person Thomas; 25.10.2014