Bagaimana cara membuat skrip dengan pelengkapan otomatis?

120

Ketika saya menggunakan program like svndan saya ketik Terminal Gnome:

svn upd

dan tekan Tabitu selesai otomatis ke:

svn update

Apakah mungkin melakukan hal seperti itu di skrip bash khusus saya?

UAdapter
sumber
jelaskan "skrip bash", maksud Anda saat mengedit skrip? apa yang ingin kamu lakukan dengan itu
Bruno Pereira
3
saat menggunakan skrip di konsol
UAdapter
Adapun tempat di mana meletakkan kelengkapan Anda, lihat pertanyaan ini dan juga komentar untuk jawaban yang diterima di sana.
jarno

Jawaban:

44

Anda bisa menggunakan Programmable Completion . Lihat /etc/bash_completiondan /etc/bash_completion.d/*untuk beberapa contoh.

Florian Diesch
sumber
131
Bagaimana dengan memasukkan contoh sederhana yang terkait langsung dengan pertanyaan?
MountainX
2
Script sebenarnya untuk Ubuntu 16 terletak di/usr/share/bash-completion/completions/<program>
peter
17
Imo, contoh harus dimasukkan dalam jawaban, bukan di tautan.
billynoah
2
Saya percaya platform ini seharusnya menjadi alternatif yang lebih praktis untuk dokumentasi lengkap yang dapat ditemukan dengan pencarian google sederhana. Membuang tautan dokumentasi tidak membantu. Tautan yang mengandung jangkar tentu tidak membuat banyak perbedaan.
timuçin
4
The provided link has that already- Mungkin hari ini, tapi mungkin besok. Atau tahun depan. Atau dalam satu dekade. Apa pun yang Anda sarankan tentang dokumentasi masih relevan, Stack Overflow mengecilkan jawaban hanya tautan karena alasan ini.
Liam Dawson
205

Saya terlambat enam bulan tetapi saya mencari hal yang sama dan menemukan ini:

Anda harus membuat file baru:

/etc/bash_completion.d/foo

Untuk pelengkapan otomatis statis ( --help/ --verbosemisalnya) tambahkan ini:

_foo() 
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts="--help --verbose --version"

    if [[ ${cur} == -* ]] ; then
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
        return 0
    fi
}
complete -F _foo foo
  • COMP_WORDS adalah array yang berisi semua kata individual di baris perintah saat ini.
  • COMP_CWORD adalah indeks kata yang berisi posisi kursor saat ini.
  • COMPREPLY adalah variabel array dari mana Bash membaca kemungkinan penyelesaian.

Dan compgenperintah mengembalikan array elemen dari --help, --verbosedan --versionmencocokkan kata saat ini "${cur}":

compgen -W "--help --verbose --version" -- "<userinput>"

Sumber: http://www.debian-administration.org/articles/316

Louis Soulez
sumber
27
Ini harus menjadi jawaban yang diterima! Ini contoh lengkap.
Victor Schröder
5
Kiat: Jika seseorang menginginkan saran untuk kata-kata yang tidak memulai -dan menunjukkannya tanpa harus mulai mengetikkan kata target, hapus saja if [...] thendan fi.
Cedric Reichenbach
1
Ini adalah jawaban tepat yang telah saya cari selama berjam-jam, dan ternyata itu hanya tenggelam dalam beberapa dokumentasi yang rumit yang tidak pernah menyebutkan bahwa file tersebut harus ditempatkan /etc/bash_completion.d/. Saya hanya datang ke sini karena saya ingin mengirim jawaban di suatu tempat, tetapi ternyata seseorang sudah tiga tahun lebih maju :) Jelas, ringkas dan contoh lengkap, terima kasih!
Steen Schütt
1
Tutorial - Membuat skrip penyelesaian bash
Lazarus Lazaridis
34

Semua penyelesaian bash disimpan di /etc/bash_completion.d/. Jadi, jika Anda sedang membangun perangkat lunak dengan bash_completion, akan bermanfaat jika Anda memiliki deb / make install drop file dengan nama perangkat lunak dalam direktori tersebut. Berikut ini contoh skrip penyelesaian bash untuk Rsync:

# bash completion for rsync

have rsync &&
_rsync()
{
    # TODO: _split_longopt

    local cur prev shell i userhost path   

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

    _expand || return 0

    case "$prev" in
    --@(config|password-file|include-from|exclude-from))
        _filedir
        return 0
        ;;
    -@(T|-temp-dir|-compare-dest))
        _filedir -d
        return 0
        ;;
    -@(e|-rsh))
        COMPREPLY=( $( compgen -W 'rsh ssh' -- "$cur" ) )
        return 0
        ;;
    esac

    case "$cur" in
    -*)
        COMPREPLY=( $( compgen -W '-v -q  -c -a -r -R -b -u -l -L -H \
            -p -o -g -D -t -S -n -W -x -B -e -C -I -T -P \
            -z -h -4 -6 --verbose --quiet --checksum \
            --archive --recursive --relative --backup \
            --backup-dir --suffix= --update --links \
            --copy-links --copy-unsafe-links --safe-links \
            --hard-links --perms --owner --group --devices\
            --times --sparse --dry-run --whole-file \
            --no-whole-file --one-file-system \
            --block-size= --rsh= --rsync-path= \
            --cvs-exclude --existing --ignore-existing \
            --delete --delete-excluded --delete-after \
            --ignore-errors --max-delete= --partial \
            --force --numeric-ids --timeout= \
            --ignore-times --size-only --modify-window= \
            --temp-dir= --compare-dest= --compress \
            --exclude= --exclude-from= --include= \
            --include-from= --version --daemon --no-detach\
            --address= --config= --port= --blocking-io \
            --no-blocking-io --stats --progress \
            --log-format= --password-file= --bwlimit= \
            --write-batch= --read-batch= --help' -- "$cur" ))
        ;;
    *:*)
        # find which remote shell is used
        shell=ssh
        for (( i=1; i < COMP_CWORD; i++ )); do
            if [[ "${COMP_WORDS[i]}" == -@(e|-rsh) ]]; then
                shell=${COMP_WORDS[i+1]}
                break
            fi
        done
        if [[ "$shell" == ssh ]]; then
            # remove backslash escape from :
            cur=${cur/\\:/:}
            userhost=${cur%%?(\\):*}
            path=${cur#*:}
            # unescape spaces
            path=${path//\\\\\\\\ / }
            if [ -z "$path" ]; then
                # default to home dir of specified
                # user on remote host
                path=$(ssh -o 'Batchmode yes' $userhost pwd 2>/dev/null)
            fi
            # escape spaces; remove executables, aliases, pipes
            # and sockets; add space at end of file names
            COMPREPLY=( $( ssh -o 'Batchmode yes' $userhost \
                command ls -aF1d "$path*" 2>/dev/null | \
                sed -e 's/ /\\\\\\\ /g' -e 's/[*@|=]$//g' \
                -e 's/[^\/]$/& /g' ) )
        fi
        ;;
    *)  
        _known_hosts_real -c -a "$cur"
        _filedir
        ;;
    esac

    return 0
} &&
complete -F _rsync $nospace $filenames rsync

# Local variables:
# mode: shell-script
# sh-basic-offset: 4
# sh-indent-comment: t
# indent-tabs-mode: nil
# End:
# ex: ts=4 sw=4 et filetype=sh

Mungkin akan bermanfaat untuk meninjau salah satu file penyelesaian bash di sana yang paling cocok dengan program Anda. Salah satu contoh paling sederhana adalah rrdtoolfile.

Marco Ceppi
sumber
2
Bisakah kita mengonfigurasi penyelesaian untuk memuat dari lokasi lain? YAITU. ~ / .local
Chris
1
Ya, Anda dapat meletakkan file seperti ini di mana pun Anda inginkan dan kemudian memasukkannya ke source ~/.local/mycrazycompletiondalam~/.bashrc
Stefano Palazzo
@ Chris lihat instruksi di Bash Completion FAQ
jarno
Saat ini sebagian besar penyelesaian terletak di direktori yang diberikan oleh perintah pkg-config --variable = completionsdir bash-completion` dan direktori itu adalah rekomendasi yang diberikan oleh Bash Completion FAQ yang ditautkan di atas.
jarno
34

Berikut ini adalah tutorial lengkapnya.

Mari kita punya contoh skrip bernama admin.sh yang ingin Anda lengkapi autocomplete.

#!/bin/bash

while [ $# -gt 0 ]; do
  arg=$1
  case $arg in
    option_1)
     # do_option_1
    ;;
    option_2)
     # do_option_1
    ;;
    shortlist)
      echo option_1 option_2 shortlist
    ;;
    *)
     echo Wrong option
    ;;
  esac
  shift
done

Catatan pilihan opsi. Skrip panggilan dengan opsi ini akan mencetak semua opsi yang memungkinkan untuk skrip ini.

Dan di sini Anda memiliki skrip autocomplete:

_script()
{
  _script_commands=$(/path/to/your/script.sh shortlist)

  local cur
  COMPREPLY=()
  cur="${COMP_WORDS[COMP_CWORD]}"
  COMPREPLY=( $(compgen -W "${_script_commands}" -- ${cur}) )

  return 0
}
complete -o nospace -F _script ./admin.sh

Perhatikan bahwa argumen terakhir yang harus diselesaikan adalah nama skrip yang ingin Anda tambahkan pelengkapan otomatis. Yang perlu Anda lakukan adalah menambahkan skrip autocomplete ke bashrc as

source /path/to/your/autocomplete.sh

atau salin ke /etc/bash.completion.d

kokosing
sumber
1
Untuk apa prevvariabelnya? Anda sepertinya tidak menggunakannya.
dimo414
@ dimo414 Sepertinya begitu, saya menghapus variabel itu
kokosing
Apa yang dilakukan -o nospaceopsi?
Andrew Lamarra
12

Jika semua yang Anda inginkan adalah penyelesaian otomatis berbasis kata sederhana (jadi tidak ada penyelesaian sub-perintah atau apa pun), completeperintah memiliki -Wopsi yang hanya melakukan hal yang benar.

Sebagai contoh, saya memiliki baris berikut di saya .bashrcuntuk melengkapi otomatis sebuah program bernama jupyter :

# gleaned from `jupyter --help`
_jupyter_options='console qtconsole notebook' # shortened for this answer
complete -W "${_jupyter_options}" 'jupyter'

Sekarang jupyter <TAB> <TAB>lengkapi otomatis untuk saya.

The docs di gnu.org membantu.

Tampaknya bergantung pada IFSvariabel yang ditetapkan dengan benar, tetapi itu tidak menyebabkan masalah bagi saya.

Untuk menambahkan penyelesaian nama file dan penyelesaian BASH default, gunakan -oopsi:

complete -W "${_jupyter_options}" -o bashdefault -o default 'jupyter'

Untuk menggunakan ini di zsh, tambahkan kode berikut sebelum menjalankan completeperintah di ~/.zshrc:

# make zsh emulate bash if necessary
if [[ -n "$ZSH_VERSION" ]]; then
    autoload bashcompinit
    bashcompinit
fi
Ben
sumber
Bagaimana saya membuat ini bekerja bash jupyter <TAB><TAB>?
papampi
@ papampi, saya pikir itu hanya bekerja dengan satu tingkat penyelesaian - Saya pikir untuk melakukannya dengan 2 lapisan Anda akan memerlukan salah satu jawaban yang lebih rumit di atas. Juga, saya baru-baru ini membaca tutorial yang cukup bagus tentang penyelesaian bash. Itu tidak melakukan apa yang Anda butuhkan, tetapi mungkin itu akan membantu Anda. Semoga berhasil!
Ben