Bagaimana saya bisa mendapatkan bash untuk melakukan penyelesaian tab untuk alias saya?

45

Saya memiliki banyak skrip penyelesaian bash yang diatur (kebanyakan menggunakan bash-it dan beberapa setup secara manual).

Saya juga punya banyak pengaturan alias untuk tugas-tugas umum seperti gcountuk git checkout. Saat ini saya dapat mengetik git checkout dTabdan developselesai untuk saya tetapi ketika saya mengetiknya gco dTabtidak lengkap.

Saya berasumsi ini karena skrip penyelesaian selesai gitdan gagal dilihat gco.

Apakah ada cara untuk secara umum / terprogram untuk mendapatkan semua skrip penyelesaian saya untuk bekerja dengan alias saya? Tidak dapat menyelesaikan saat menggunakan jenis alias mengalahkan tujuan alias.

dstarh
sumber
OS dan bash apa yang Anda gunakan? Saya di Ubuntu 11.10 dan bash 4.2.10 (1) -release (x86_64-pc-linux-gnu) dan saya memiliki fungsi ini dibangun di shell saya untuk banyak alias saya. btw bash --versionuntuk mendapatkan ini (jangan gunakan -v, output berbeda).
Michael Durrant
Maaf melewatkan sedikit info - OSX Lion, GNU bash, versi 3.2.48 (1) -release (x86_64-apple-darwin11)
dstarh
1
@killermist: kecuali saya benar-benar salah, zsh juga tidak menyelesaikan perintah alias di luar kotak. Menerapkan fungsi yang menambahkan alias yang telah ditentukan ke penyelesaian tampaknya jauh lebih mudah daripada untuk bash, meskipun, karena sistem penyelesaian zhs tampaknya lebih kuat dan lebih mudah daripada bash.
kopischke
Duplikat lintas situs: stackoverflow.com/questions/342969/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
1
@MichaelDurrant Apakah Anda yakin ini sebenarnya dibangun untuk alias? Saya di Ubuntu 15.10 dengan Bash 4.3.42 (1) -release (x86_64-pc-linux-gnu) dan tidak ada yang seperti itu. Saya juga menguji beberapa rilis sebelumnya. Jadi misalnya jika Anda mengetiknya ll --[TAB]akan mencetak daftar opsi untuk ls? Saya cukup skeptis dengan hal ini, tetapi jika Anda yakin hal seperti itu ada pada jam 11.10 saya ingin tahu untuk menggali dan menentukan apa yang telah dihapus.
Enam

Jawaban:

42

Kode berikut, diadaptasi dari jawaban Stack Overflow ini dan utas diskusi Forum Ubuntu ini akan menambah penyelesaian untuk semua alias yang Anda tentukan:

# Automatically add completion for all aliases to commands having completion functions
function alias_completion {
    local namespace="alias_completion"

    # parse function based completion definitions, where capture group 2 => function and 3 => trigger
    local compl_regex='complete( +[^ ]+)* -F ([^ ]+) ("[^"]+"|[^ ]+)'
    # parse alias definitions, where capture group 1 => trigger, 2 => command, 3 => command arguments
    local alias_regex="alias ([^=]+)='(\"[^\"]+\"|[^ ]+)(( +[^ ]+)*)'"

    # create array of function completion triggers, keeping multi-word triggers together
    eval "local completions=($(complete -p | sed -Ene "/$compl_regex/s//'\3'/p"))"
    (( ${#completions[@]} == 0 )) && return 0

    # create temporary file for wrapper functions and completions
    rm -f "/tmp/${namespace}-*.tmp" # preliminary cleanup
    local tmp_file; tmp_file="$(mktemp "/tmp/${namespace}-${RANDOM}XXX.tmp")" || return 1

    local completion_loader; completion_loader="$(complete -p -D 2>/dev/null | sed -Ene 's/.* -F ([^ ]*).*/\1/p')"

    # read in "<alias> '<aliased command>' '<command args>'" lines from defined aliases
    local line; while read line; do
        eval "local alias_tokens; alias_tokens=($line)" 2>/dev/null || continue # some alias arg patterns cause an eval parse error
        local alias_name="${alias_tokens[0]}" alias_cmd="${alias_tokens[1]}" alias_args="${alias_tokens[2]# }"

        # skip aliases to pipes, boolean control structures and other command lists
        # (leveraging that eval errs out if $alias_args contains unquoted shell metacharacters)
        eval "local alias_arg_words; alias_arg_words=($alias_args)" 2>/dev/null || continue
        # avoid expanding wildcards
        read -a alias_arg_words <<< "$alias_args"

        # skip alias if there is no completion function triggered by the aliased command
        if [[ ! " ${completions[*]} " =~ " $alias_cmd " ]]; then
            if [[ -n "$completion_loader" ]]; then
                # force loading of completions for the aliased command
                eval "$completion_loader $alias_cmd"
                # 124 means completion loader was successful
                [[ $? -eq 124 ]] || continue
                completions+=($alias_cmd)
            else
                continue
            fi
        fi
        local new_completion="$(complete -p "$alias_cmd")"

        # create a wrapper inserting the alias arguments if any
        if [[ -n $alias_args ]]; then
            local compl_func="${new_completion/#* -F /}"; compl_func="${compl_func%% *}"
            # avoid recursive call loops by ignoring our own functions
            if [[ "${compl_func#_$namespace::}" == $compl_func ]]; then
                local compl_wrapper="_${namespace}::${alias_name}"
                    echo "function $compl_wrapper {
                        (( COMP_CWORD += ${#alias_arg_words[@]} ))
                        COMP_WORDS=($alias_cmd $alias_args \${COMP_WORDS[@]:1})
                        (( COMP_POINT -= \${#COMP_LINE} ))
                        COMP_LINE=\${COMP_LINE/$alias_name/$alias_cmd $alias_args}
                        (( COMP_POINT += \${#COMP_LINE} ))
                        $compl_func
                    }" >> "$tmp_file"
                    new_completion="${new_completion/ -F $compl_func / -F $compl_wrapper }"
            fi
        fi

        # replace completion trigger by alias
        new_completion="${new_completion% *} $alias_name"
        echo "$new_completion" >> "$tmp_file"
    done < <(alias -p | sed -Ene "s/$alias_regex/\1 '\2' '\3'/p")
    source "$tmp_file" && rm -f "$tmp_file"
}; alias_completion

Untuk alias sederhana (hanya perintah, tanpa argumen), ia akan menetapkan fungsi penyelesaian asli ke alias; untuk alias dengan argumen, itu menciptakan fungsi wrapper yang menyisipkan argumen tambahan ke fungsi penyelesaian asli.

Tidak seperti skrip tempat ia berevolusi, fungsinya menghormati tanda kutip untuk perintah alias dan argumennya (tetapi yang pertama harus dicocokkan dengan perintah penyelesaian, dan tidak dapat disarangkan), dan ia harus dapat dengan baik menyaring alias ke daftar perintah dan pipa (yang dilewati, karena tidak mungkin untuk menemukan apa yang harus diselesaikan di dalamnya tanpa membuat ulang logika parsing baris perintah shell lengkap).

Pemakaian

Simpan kode sebagai file skrip shell dan sumber yang ada di, atau salin fungsi grosir ke, .bashrc(atau file dot terkait Anda ). Yang penting adalah memanggil fungsi setelah selesai bash dan alias definisi telah diatur (kode di atas memanggil fungsi tepat setelah definisinya, dalam semangat "sumber dan lupa", tetapi Anda dapat memindahkan panggilan ke mana saja di hilir jika itu lebih cocok untukmu). Jika Anda tidak ingin fungsi di lingkungan Anda setelah keluar, Anda dapat menambahkan unset -f alias_completionsetelah memanggilnya.

Catatan

Jika Anda menggunakan bash4.1 atau lebih dan menggunakan penyelesaian yang dimuat secara dinamis, skrip akan mencoba memuat penyelesaian untuk semua perintah alias Anda sehingga dapat membangun fungsi pembungkus untuk alias Anda.

kopischke
sumber
1
Bagaimana cara saya menginstal skrip itu?
Der Hochstapler
1
@OliverSalzburg: Anda harus memrosesnya di salah satu file profil shell Anda, yang terpenting setelah bash selesai - itu mungkin akan membuatnya ~/.bashrc. Baik menyimpannya sebagai file skrip shell dan sumbernya ( . /path/to/alias_completion.sh), atau menyalin dan menempelkan kode grosir.
kopischke
1
@OliverSalzburg: menambahkan instruksi penggunaan (tidak langsung menyadari bahwa Anda bukan OP).
kopischke
1
@kopischke Lihat pertanyaan ini - tampaknya untuk file-file di bawahnya /usr/share/bash-completion/completions/hanya dimuat pertama kali pengguna benar-benar memukul [TAB]. Jadi, bahkan jika fungsi tersebut diambil dari ~/.bashrcitu tidak akan menghasilkan penyelesaian untuk alias untuk perintah di dalamnya. Setelah memastikan complete -pberfungsi apt-getdan apt-cachesaya salin-tempelkan fungsi Anda ke terminal dan berfungsi dengan benar.
jamadagni
1
@kopischke Jadi saya tidak yakin bagaimana memaksakan sumber dari semua file penyelesaian yang dimuat secara dinamis, atau bahkan jika itu disarankan. Untuk saat ini saya telah menyalin file penyelesaian yang dihasilkan dari /tmpke ~/.bash_completiondan secara manual ditambahkan di awal source /usr/share/bash-completion/completions/entri yang relevan (secara terpisah untuk apt-getdan apt-cache- apt-{cache,get}tidak berfungsi).
jamadagni
4

Apakah ada cara untuk secara umum / terprogram untuk mendapatkan semua skrip penyelesaian saya untuk bekerja dengan alias saya?

Ya, inilah proyek lengkap alias yang menyelesaikan masalah Anda dengan tepat. Ini memberikan penyelesaian alias generik dan terprogram tanpa menggunakan eval.

Cyker
sumber
2

Ini adalah cara manual, bagi mereka yang mencari ini.

Pertama, cari perintah penyelesaian asli. Contoh:

$ complete | grep git

complete -o bashdefault -o default -o nospace -F __git_wrap__git_main git

Sekarang tambahkan ini ke skrip startup Anda (mis. ~ / .Bashrc):

# load dynamically loaded completion functions (may not be required)
_completion_loader git

# copy the original statement, but replace the last command (git) with your alias (g)
complete -o bashdefault -o default -o nospace -F __git_wrap__git_main g

sumber: https://superuser.com/a/1004334

Wisbucky
sumber