Làm thế nào để tùy chỉnh hoàn thành lệnh Bash?


24

Trong bash, thật dễ dàng để thiết lập hoàn thành tùy chỉnh các đối số lệnh bằng cách sử dụng tích completehợp. Ví dụ: nếu, đối với một lệnh giả định có tóm tắt foo --a | --b | --c, bạn có thể làmcomplete -W '--a --b --c' foo

Bạn cũng có thể tùy chỉnh mức độ hoàn thành bạn nhận được khi nhấn Tabvào dấu nhắc trống bằng cách sử dụng complete -E, ví dụ : complete -E -W 'foo bar'. Sau đó nhấn tab tại dấu nhắc trống sẽ chỉ đề xuất foobar.

Làm thế nào để tùy chỉnh hoàn lệnh tại một phi -empty nhắc? Ví dụ: nếu tôi đang ngồi tại:

anthony@Zia:~$ f

Làm thế nào để tùy chỉnh hoàn thành để nhấn tab luôn luôn hoàn thành foo?

(Trường hợp thực tế tôi muốn là locTABlocalc. Và anh trai tôi, người đã nhắc tôi hỏi điều này, muốn nó với mplayer).


2
Bạn đã xem xét đơn giản là răng cưa locđể localc? Tôi đề nghị các lựa chọn thay thế bởi vì sau một thời gian đào bới và tìm kiếm, tôi không tìm thấy cách nào để tùy chỉnh hoàn thành bash theo cách này. Nó có thể là không thể.
jw013

@ jw013 Vâng, tôi đã tìm một lúc và cũng không thể tìm thấy gì. Tôi cũng đang mong đợi ai đó đề nghị chuyển sang zsh. Ít nhất nếu zsh làm điều đó, tôi có thể thoải mái nghỉ ngơi khi biết phiên bản tiếp theo của bash cũng vậy. 😀
derobert

bạn phải nhấn TAB hai lần và nó sẽ hoàn thành các lệnh.
Floyd

1
Sẽ không phá vỡ tất cả các hoàn thành khác mặc dù? Ý tôi là, ngay cả khi nó là có thể, bạn sẽ không còn có thể nhận được locate, locale, lockfilehoặc bất kỳ các mở rộng khác loc. Có lẽ một cách tiếp cận tốt hơn sẽ là ánh xạ một chìa khóa khác để hoàn thành cụ thể đó.
terdon

1
bạn có thể đưa ra một ví dụ về bối cảnh như vậy (điều đó sẽ dẫn đến hiệu quả mong muốn loc<TAB>->localc)?
damienfrancois

Câu trả lời:


9

Hoàn thành lệnh (cùng với những thứ khác) được xử lý thông qua hoàn thành bash readline . Điều này hoạt động ở mức thấp hơn một chút so với "hoàn thành lập trình" thông thường (chỉ được gọi khi lệnh được xác định và hai trường hợp đặc biệt bạn đã xác định ở trên).

Cập nhật: bản phát hành mới của bash-5.0 (tháng 1 năm 2019) bổ sung complete -Ichính xác cho vấn đề này.

Các lệnh đọc có liên quan là:

complete (TAB)
       Attempt to perform completion on the text  before  point.   Bash
       attempts completion treating the text as a variable (if the text
       begins with $), username (if the text begins with  ~),  hostname
       (if  the  text begins with @), or command (including aliases and
       functions) in turn.  If none of these produces a match, filename
       completion is attempted.

complete-command (M-!)
       Attempt  completion  on  the text before point, treating it as a
       command name.  Command completion attempts  to  match  the  text
       against   aliases,   reserved   words,  shell  functions,  shell
       builtins, and finally executable filenames, in that order.

Theo cách tương tự như phổ biến hơn complete -F, một số trong số này có thể được chuyển giao cho một chức năng bằng cách sử dụng bind -x.

function _complete0 () {
    local -a _cmds
    local -A _seen
    local _path=$PATH _ii _xx _cc _cmd _short
    local _aa=( ${READLINE_LINE} )

    if [[ -f ~/.complete.d/"${_aa[0]}" && -x  ~/.complete.d/"${_aa[0]}" ]]; then
        ## user-provided hook
        _cmds=( $( ~/.complete.d/"${_aa[0]}" ) )
    elif [[ -x  ~/.complete.d/DEFAULT ]]; then
        _cmds=( $( ~/.complete.d/DEFAULT ) )
    else 
        ## compgen -c for default "command" complete 
        _cmds=( $(PATH=$_path compgen -o bashdefault -o default -c ${_aa[0]}) )  
    fi

    ## remove duplicates, cache shortest name
    _short="${_cmds[0]}"
    _cc=${#_cmds[*]} # NB removing indexes inside loop
    for (( _ii=0 ; _ii<$_cc ; _ii++ )); do
        _cmd=${_cmds[$_ii]}
        [[ -n "${_seen[$_cmd]}" ]] && unset _cmds[$_ii]
        _seen[$_cmd]+=1
        (( ${#_short} > ${#_cmd} )) && _short="$_cmd"
    done
    _cmds=( "${_cmds[@]}" )  ## recompute contiguous index

    ## find common prefix
    declare -a _prefix=()
    for (( _xx=0; _xx<${#_short}; _xx++ )); do
        _prev=${_cmds[0]}
        for (( _ii=0 ; _ii<${#_cmds[*]} ; _ii++ )); do
            _cmd=${_cmds[$_ii]}
             [[ "${_cmd:$_xx:1}" != "${_prev:$_xx:1}" ]] && break
            _prev=$_cmd
        done
        [[ $_ii -eq ${#_cmds[*]} ]] && _prefix[$_xx]="${_cmd:$_xx:1}"
    done
    printf -v _short "%s" "${_prefix[@]}"  # flatten 

    ## emulate completion list of matches
    if [[ ${#_cmds[*]} -gt 1 ]]; then
        for (( _ii=0 ; _ii<${#_cmds[*]} ; _ii++ )); do
            _cmd=${_cmds[$_ii]}
            [[ -n "${_seen[$_cmds]}" ]] && printf "%-12s " "$_cmd" 
        done | sort | fmt -w $((COLUMNS-8)) | column -tx
        # fill in shortest match (prefix)
        printf -v READLINE_LINE "%s" "$_short"
        READLINE_POINT=${#READLINE_LINE}  
    fi
    ## exactly one match
    if [[ ${#_cmds[*]} -eq 1 ]]; then
        _aa[0]="${_cmds[0]}"
        printf -v READLINE_LINE "%s " "${_aa[@]}"
        READLINE_POINT=${#READLINE_LINE}  
    else
        : # nop
    fi
}

bind -x '"\C-i":_complete0'

Điều này cho phép móc chuỗi tiền tố hoặc tiền tố của riêng bạn vào ~/.complete.d/. Ví dụ: nếu bạn tạo một tệp thực thi ~/.complete.d/locvới:

#!/bin/bash
echo localc

Điều này sẽ làm (khoảng) những gì bạn mong đợi.

Hàm trên có một số độ dài để mô phỏng hành vi hoàn thành lệnh bash bình thường, mặc dù nó không hoàn hảo (đặc biệt là phần sort | fmt | columnmang theo đáng ngờ để hiển thị danh sách các trận đấu).

Tuy nhiên , một vấn đề không hề nhỏ với vấn đề này, nó chỉ có thể sử dụng một hàm để thay thế liên kết thành completehàm chính (được gọi bằng TAB theo mặc định).

Cách tiếp cận này sẽ hoạt động tốt với một ràng buộc khóa khác được sử dụng để hoàn thành lệnh tùy chỉnh, nhưng đơn giản là nó không thực hiện logic hoàn thành đầy đủ sau đó (ví dụ: các từ sau trong dòng lệnh). Làm như vậy sẽ yêu cầu phân tích dòng lệnh, xử lý vị trí con trỏ và những thứ khó khăn khác có lẽ không nên được xem xét trong tập lệnh shell ...


1

Tôi không biết nếu tôi hiểu nhu cầu của bạn về điều này ...
Điều này có nghĩa là bash của bạn chỉ biết một lệnh bắt đầu bằng f.
Một ý tưởng cơ bản của việc hoàn thành là: nếu nó mơ hồ, hãy in những khả năng.
Vì vậy, bạn có thể thiết lập PATHmột thư mục chỉ chứa một lệnh này và vô hiệu hóa tất cả các nội dung bash để có được công việc này.

Dù sao, tôi cũng có thể cung cấp cho bạn một loại giải pháp:

alias _='true &&'
complete -W foo _

Vì vậy, nếu bạn gõ _ <Tab>nó sẽ hoàn thành để _ foothực thi foo.

Nhưng tuy nhiên alias f='foo'sẽ dễ dàng hơn nhiều.


0

Câu trả lời đơn giản cho bạn sẽ là

$ cd into /etc/bash_completion.d
$ ls

chỉ là đầu ra cơ bản

autoconf       gpg2               ntpdate           shadow
automake       gzip               open-iscsi        smartctl
bash-builtins  iconv              openssl           sqlite3
bind-utils     iftop              perl              ssh
brctl          ifupdown           pkg-config        strace
bzip2          info               pm-utils          subscription-manager
chkconfig      ipmitool           postfix           tar
configure      iproute2           procps            tcpdump
coreutils      iptables           python            util-linux
cpio           lsof               quota-tools       wireless-tools
crontab        lvm                redefine_filedir  xmllint
cryptsetup     lzma               rfkill            xmlwf
dd             make               rpm               xz
dhclient       man                rsync             yum.bash
e2fsprogs      mdadm              scl.bash          yum-utils.bash
findutils      module-init-tools  service
getent         net-tools          sh

chỉ cần thêm chương trình mong muốn của bạn để tự động hoàn thành để hoàn thành bash


2
Những tập tin đó có tập lệnh shell trong đó, một số khá phức tạp nếu tôi nhớ chính xác. Ngoài ra, họ chỉ hoàn thành các đối số khi bạn đã gõ lệnh ... Họ không hoàn thành lệnh.
derobert

Tôi hiểu ý của bạn Khi bạn có thể xem tài liệu này: debian
ad dùng.org / article / 310 / U

-3

Chạy lệnh dưới đây để tìm nơi cài đặt nhị phân mplayer:

which mplayer

HOẶC sử dụng đường dẫn đến nhị phân mplayer nếu bạn biết điều đó, trong lệnh dưới đây:

ln -s /path/to/mplayer /bin/mplayer

Lý tưởng nhất là bất cứ điều gì bạn gõ được tìm kiếm trong tất cả các thư mục được chỉ định trong $PATHbiến.


2
Xin chào, Mathew, chào mừng bạn đến với Unix & Linux. Tôi nghĩ rằng câu hỏi của OP phức tạp hơn một chút so với câu trả lời mà bạn đề xuất. Tôi đoán mplayerlà đã có sẵn của họ $PATHvà họ muốn một cái gì đó muốn mp<tab>sản xuất mplayer, thay vì tất cả các nhị phân bắt đầu bằng mp(ví dụ, mpage mpcd mpartition mplayerv.v.)
drs
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.