So passen Sie die Bash-Befehlsergänzung an

24

In bash ist es einfach genug, die benutzerdefinierte Vervollständigung von Befehlsargumenten mithilfe der completeintegrierten Funktion einzurichten . Zum Beispiel, wenn Sie für einen hypothetischen Befehl mit einer Zusammenfassung von foo --a | --b | --ctun könntencomplete -W '--a --b --c' foo

Sie können auch die Vervollständigung anpassen, die Sie erhalten, wenn Sie Taban einer leeren Eingabeaufforderung auf drücken complete -E, z complete -E -W 'foo bar'. B .. Dann würde das Drücken der Tabulatortaste an der leeren Eingabeaufforderung nur foound vorschlagen bar.

Wie passe ich die Befehlsausführung an einer nicht leeren Eingabeaufforderung an? ZB wenn ich sitze bei:

anthony@Zia:~$ f

Wie kann die Vervollständigung angepasst werden, sodass das Drücken der Tabulatortaste immer zu vervollständigt wird foo?

(Der tatsächliche Fall, den ich möchte, ist locTABlocalc. Und mein Bruder, der mich dazu aufgefordert hat, dies zu fragen, möchte es mit mplayer).

derobert
quelle
2
Haben Sie schon einmal darüber nachgedacht, ein Alias loczu erstellen localc? Ich schlage Alternativen vor, da ich nach einiger Zeit beim Graben und Suchen keine Möglichkeit gefunden habe, die Bash-Vervollständigung auf diese Weise anzupassen. Möglicherweise ist dies nicht möglich.
Jw013
@ jw013 Ja, ich habe eine Weile gesucht und auch nichts gefunden. Ich erwarte zur Hälfte, dass jemand auch einen Wechsel zu zsh vorschlägt. Zumindest, wenn zsh es tut, kann ich mich beruhigt ausruhen und weiß, dass auch die nächste Version von bash funktioniert. 😀
derobert
Sie müssen zweimal die Tabulatortaste drücken, um die Befehle auszuführen.
Floyd
1
Bricht das nicht alle anderen Abschlüsse? Ich meine, selbst wenn es möglich ist, würden Sie nicht mehr in der Lage sein zu bekommen locate, locale, lockfileoder eine der anderen Erweiterungen loc. Vielleicht wäre es ein besserer Ansatz, einen anderen Schlüssel für diese bestimmte Vervollständigung abzubilden.
terdon
1
Kannst du ein Beispiel für einen solchen Kontext geben (der zum gewünschten Effekt führen würde loc<TAB>->localc)?
Damienfrancois

Antworten:

9

Die Vervollständigung des Befehls (zusammen mit anderen Dingen) wird über die Bash Readline-Vervollständigung abgewickelt . Dies funktioniert auf einer etwas niedrigeren Ebene als die übliche "programmierbare Vervollständigung" (die nur aufgerufen wird, wenn der Befehl identifiziert wurde und die beiden oben angegebenen Sonderfälle).

Update: Die neue Version von bash-5.0 (Januar 2019) ergänzt complete -Igenau dieses Problem.

Die relevanten Readline-Befehle sind:

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.

In ähnlicher Weise wie üblich complete -Fkann ein Teil davon durch Verwendung von an eine Funktion übergeben werden 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'

Dies aktiviert Ihre eigenen Befehle oder Präfixzeichenfolgen ~/.complete.d/. ZB wenn Sie eine ausführbare Datei erstellen ~/.complete.d/locmit:

#!/bin/bash
echo localc

Dies wird (ungefähr) das tun, was Sie erwarten.

Die obige Funktion emuliert das normale Verhalten beim Beenden von Bash-Befehlen, ist jedoch unvollkommen (insbesondere das zweifelhafte Vorgehen sort | fmt | columnbeim Anzeigen einer Liste von Übereinstimmungen).

Ein nicht triviales Problem, bei dem nur eine Funktion zum Ersetzen der Bindung an die Hauptfunktion verwendet werden completekann (standardmäßig mit TAB aufgerufen).

Dieser Ansatz würde gut mit einer anderen Schlüsselbindung funktionieren, die nur für die Vervollständigung von benutzerdefinierten Befehlen verwendet wird, implementiert jedoch einfach nicht die Logik für die vollständige Vervollständigung danach (z. B. spätere Wörter in der Befehlszeile). Dazu müsste man die Befehlszeile analysieren, sich mit der Cursorposition und anderen kniffligen Dingen befassen, die in einem Shell-Skript wahrscheinlich nicht berücksichtigt werden sollten ...

mr.spuratic
quelle
1

Ich weiß nicht, ob ich Ihr Bedürfnis danach unterstütze ...
Dies würde bedeuten, dass Ihre Bash nur einen Befehl kennt, der mit beginnt f.
Eine Grundidee der Vervollständigung ist: Wenn es mehrdeutig ist, drucken Sie die Möglichkeiten.
Sie können also PATHein Verzeichnis festlegen, das nur diesen einen Befehl enthält, und alle integrierten Bash-Funktionen deaktivieren, um diese Funktion zu erhalten.

Jedenfalls kann ich Ihnen auch eine Art Workaround geben:

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

Wenn Sie also Folgendes _ <Tab>eingeben, wird abgeschlossen, _ foowas ausgeführt wird foo.

Aber das alias f='foo'wäre doch viel einfacher.

m13r
quelle
0

Einfache Antwort für Sie wäre zu

$ cd into /etc/bash_completion.d
$ ls

nur die Grundausgänge

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

Fügen Sie einfach Ihr gewünschtes Programm hinzu, um es automatisch zu vervollständigen

unixmiah
quelle
2
Diese Dateien enthalten Shell-Skripte, von denen einige recht komplex sind, wenn ich mich richtig erinnere. Außerdem vervollständigen sie Argumente erst, wenn Sie den Befehl bereits eingegeben haben. Sie vervollständigen den Befehl nicht.
Derobert
Ich verstehe was du meinst. Wenn Sie einen Blick auf dieses Dokument werfen können
unixmiah
-3

Führen Sie den folgenden Befehl aus, um herauszufinden, wo mplayer binary installiert ist:

which mplayer

ODER verwenden Sie den Pfad zur mplayer-Binärdatei, wenn Sie dies bereits wissen, im folgenden Befehl:

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

Im Idealfall wird alles, was Sie eingeben, in allen in der $PATHVariablen angegebenen Verzeichnissen durchsucht .

Mathew Paret
quelle
2
Hallo Mathew, willkommen bei Unix & Linux. Ich denke, die Frage des OP ist etwas komplizierter als die Antwort, die Sie vorschlagen. Ich nehme an, mplayerist bereits in ihrer $PATHund sie möchten etwas wie mp<tab>produzieren mplayer, anstatt alle Binärdateien, die mit mp(z. B. mpage mpcd mpartition mplayerusw.) beginnen
drs