Làm thế nào để tạo tập lệnh với tự động hoàn thành?


120

Khi tôi sử dụng chương trình thích svnvà tôi gõ vào Gnome Terminal:

svn upd

và nhấn Tabnó tự động hoàn thành:

svn update

Có thể làm một cái gì đó như thế trong kịch bản bash tùy chỉnh của tôi?


giải thích "bash script", ý bạn là khi chỉnh sửa tập lệnh? Bạn muốn làm gì với cái này?
Bruno Pereira

3
khi sử dụng tập lệnh trong bảng điều khiển
UAd CHƯƠNG

Đối với nơi để đặt hoàn thành của bạn, xem câu hỏi này và các ý kiến ​​cho câu trả lời được chấp nhận ở đó.
Jarno

Câu trả lời:


44

Bạn có thể sử dụng Hoàn thành lập trình . Hãy nhìn vào /etc/bash_completion/etc/bash_completion.d/*cho một số ví dụ.


131
Làm thế nào về việc bao gồm một ví dụ đơn giản liên quan trực tiếp đến câu hỏi?
MountainX

2
Các tập lệnh thực tế cho Ubuntu 16 được đặt trong/usr/share/bash-completion/completions/<program>
peter

17
Imo, ví dụ nên được đưa vào câu trả lời, không phải trong một liên kết.
billynoah

2
Tôi tin rằng nền tảng này được cho là một sự thay thế thực tế hơn cho các tài liệu đầy đủ có thể được tìm thấy với một tìm kiếm google đơn giản. Bán phá giá một liên kết tài liệu không giúp điều đó. Liên kết chứa một neo chắc chắn không tạo ra nhiều khác biệt.
timuçin

4
The provided link has that already- nó có thể hôm nay, nhưng nó có thể không phải ngày mai. Hoặc năm tới. Hoặc trong một thập kỷ. Bất cứ điều gì bạn có thể đề xuất về tài liệu vẫn có liên quan, Stack Overflow không khuyến khích các câu trả lời chỉ liên kết vì những lý do này.
Liam Dawson

205

Tôi trễ sáu tháng nhưng tôi đang tìm kiếm điều tương tự và tìm thấy điều này:

Bạn sẽ phải tạo một tệp mới:

/etc/bash_completion.d/foo

Đối với tự động hoàn thành tĩnh ( --help/ --verboseví dụ), hãy thêm điều này:

_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 là một mảng chứa tất cả các từ riêng lẻ trong dòng lệnh hiện tại.
  • COMP_CWORD là một chỉ mục của từ chứa vị trí con trỏ hiện tại.
  • COMPREPLY là một biến mảng mà Bash đọc các hoàn thành có thể.

compgenlệnh trả về mảng các yếu tố từ --help, --verbose--versionphù hợp với từ hiện tại "${cur}":

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

Nguồn: http : //www.debian-adftime.org/articles/316


27
Đây phải là câu trả lời được chấp nhận! Đó là một ví dụ hoàn chỉnh.
Victor Schröder

5
Mẹo: Nếu ai đó muốn đề xuất cho các từ không bắt đầu -và hiển thị chúng mà không phải bắt đầu nhập từ mục tiêu, chỉ cần xóa if [...] thenficác dòng.
Cedric Reichenbach

1
Đây là câu trả lời chính xác mà tôi đã tìm kiếm trong nhiều giờ, và hóa ra nó chỉ bị nhấn chìm trong một số tài liệu phức tạp mà không bao giờ đề cập đến việc tập tin nên được đặt vào /etc/bash_completion.d/. Tôi chỉ đến đây vì tôi muốn đăng câu trả lời ở đâu đó, nhưng hóa ra ai đó đã đi trước ba năm :) Ví dụ rõ ràng, súc tích và đầy đủ, cảm ơn bạn!
Steen Schütt


34

Tất cả các bash hoàn thành được lưu trữ trong /etc/bash_completion.d/. Vì vậy, nếu bạn đang xây dựng phần mềm với bash_completion, sẽ rất đáng để deb / make install thả một tệp có tên của phần mềm trong thư mục đó. Dưới đây là một kịch bản hoàn thành bash ví dụ cho 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

Rất có thể đáng để xem xét một trong các tệp hoàn thành bash trong đó phù hợp nhất với chương trình của bạn. Một trong những ví dụ đơn giản nhất là rrdtooltệp.


2
Chúng tôi có thể định cấu hình hoàn thành để tải từ các vị trí khác không? I E. ~ / .local
Chris

1
Có, bạn có thể đặt một tệp như thế này bất cứ nơi nào bạn muốn và sau đó đặt source ~/.local/mycrazycompletionvào~/.bashrc
Stefano Palazzo


Ngày nay, hầu hết các phần hoàn thành đều nằm trong thư mục được cung cấp bởi lệnh pkg-config --variable = xongionsdir bash-xong` và thư mục đó là đề xuất được đưa ra bởi Bash Hoàn thành Câu hỏi thường gặp được liên kết ở trên.
Jarno

34

Đây là một hướng dẫn đầy đủ.

Hãy có một ví dụ về tập lệnh được gọi là admin.sh mà bạn muốn tự động hoàn thành.

#!/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

Lưu ý danh sách rút gọn tùy chọn. Gọi tập lệnh với tùy chọn này sẽ in ra tất cả các tùy chọn có thể cho tập lệnh này.

Và ở đây bạn có kịch bản tự động hoàn thành:

_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

Lưu ý rằng đối số cuối cùng cần hoàn thành là tên của tập lệnh bạn muốn thêm tự động hoàn thành. Tất cả những gì bạn cần làm là thêm tập lệnh tự động hoàn tất vào bashrc như

source /path/to/your/autocomplete.sh

hoặc sao chép nó vào /etc/bash.completion.d


1
Các prevbiến cho là gì? Bạn dường như không sử dụng nó.
dimo414

@ dimo414 Có vẻ như vậy, tôi đã xóa biến đó
kokizing

Không những gì -o nospacelựa chọn làm gì?
Andrew Lamarra

11

Nếu tất cả những gì bạn muốn là tự động hoàn thành dựa trên từ đơn giản (vì vậy không hoàn thành tiểu ban hoặc bất cứ điều gì), completelệnh có một -Wtùy chọn chỉ thực hiện đúng.

Ví dụ: tôi có các dòng sau .bashrcđể tự động hoàn thành một chương trình có tên là jupyter :

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

Bây giờ jupyter <TAB> <TAB>tự động hoàn thành cho tôi.

Các tài liệu tại gnu.org là hữu ích.

Nó dường như dựa vào IFSbiến được đặt chính xác, nhưng điều đó không gây ra bất kỳ vấn đề nào cho tôi.

Để thêm hoàn thành tên tệp và hoàn thành BASH mặc định, hãy sử dụng -otùy chọn:

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

Để sử dụng điều này trong zsh, hãy thêm đoạn mã sau trước khi chạy completelệnh trong ~/.zshrc:

# make zsh emulate bash if necessary
if [[ -n "$ZSH_VERSION" ]]; then
    autoload bashcompinit
    bashcompinit
fi

Làm thế nào để tôi thực hiện công việc này với bash jupyter <TAB><TAB>?
papampi

@papampi, tôi nghĩ rằng nó chỉ hoạt động với một cấp độ hoàn thành - Tôi nghĩ để làm điều đó với 2 lớp bạn cần một trong những câu trả lời phức tạp hơn ở trên. Ngoài ra, gần đây tôi đã đọc một hướng dẫn khá tốt về hoàn thành bash. Nó không làm chính xác những gì bạn cần, nhưng có lẽ nó sẽ giúp bạn. Chúc may mắn!
Ben
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.