Bash-Vervollständigung für Dateinamenmuster oder Verzeichnisse

12

Ich versuche, ein Skript zur Bash-Vervollständigung einzurichten, und habe einige Probleme.

Ich möchte es so einrichten, dass die aufgeführten Vervollständigungen entweder Dateien sind, die einer bestimmten Erweiterung entsprechen, oder Verzeichnisse (die Dateien dieser Erweiterung enthalten können oder nicht).

Das Problem ist, dass ich die Vervollständigungen nur mit so etwas wie Dateien und Verzeichnissen abrufen kann. -o plusdirs -f -X '!*.txt'Wenn ich jedoch zulasse, dass Bash eines der Verzeichnisse vervollständigt, wird statt eines Leerzeichens ein Leerzeichen am Ende eingefügt Schrägstrich.

_xyz()
{
  local cur=${COMP_WORDS[COMP_CWORD]}
  local prev=${COMP_WORDS[COMP_CWORD-1]}

  #COMPREPLY=( $( compgen -f -X '!*.txt' -- $cur ) )
  #COMPREPLY=( $( compgen -f -G '*.txt' -- $cur ) )
  #COMPREPLY=( $( compgen -o filenames -f -X '!*.txt' -- $cur ) )
  #COMPREPLY=( $( compgen -o dirnames  -f -X '!*.txt' -- $cur ) )
  COMPREPLY=( $( compgen -o plusdirs  -f -X '!*.txt' -- $cur ) )
  return 0
}

complete -F _xyz xyz

Ich habe auch alle auskommentierten Zeilen ausprobiert, aber sie erweitern nicht einmal die Verzeichnisse.

Zum Testen habe ich dies in einem Verzeichnis mit einer TXT-Datei und einem Verzeichnis "dir" ausgeführt (mit einer TXT-Datei darin, obwohl das noch keine Rolle spielt). Wenn Sie xyz <TAB>mit dieser Funktion tippen, werden das Verzeichnis und die TXT-Datei aufgelistet, die Eingabe wird jedoch auf xyz d<TAB>erweitert xyz dir(also mit einem Leerzeichen nach "dir").

Rob ich
quelle

Antworten:

10

Wenn Sie sich die Funktion _cd()in / etc / bash_completion ansehen , werden Sie feststellen , dass der abschließende Schrägstrich selbst angehängt wird und dass complete mit der Option -o nospacefür cd aufgerufen wird .

Sie können dasselbe für xyz tun , müssen jedoch separat prüfen , ob die gefundene Übereinstimmung ein Verzeichnis ist (wenn ja, Schrägstrich anhängen) oder eine Datei (wenn ja, Leerzeichen anhängen). Dies sollte in einer for-Schleife erfolgen, um alle gefundenen Übereinstimmungen zu verarbeiten.

Um Pfade, die Leerzeichen enthalten, richtig zu behandeln, müssen Sie das interne Dateitrennzeichen so einstellen, dass nur Zeilenumbrüche und Leerzeichen vermieden werden. Unter Verwendung IFS=$'\n'in Kombination mit printf %qMarken Abschluss der Arbeit mit fast allen Zeichen. 1 Es ist besonders darauf zu achten, dass der nachlaufende Raum nicht verlassen wird.

Folgendes sollte funktionieren:

_xyz ()
{
    local IFS=$'\n'
    local LASTCHAR=' '

    COMPREPLY=($(compgen -o plusdirs -f -X '!*.txt' \
        -- "${COMP_WORDS[COMP_CWORD]}"))

    if [ ${#COMPREPLY[@]} = 1 ]; then
        [ -d "$COMPREPLY" ] && LASTCHAR=/
        COMPREPLY=$(printf %q%s "$COMPREPLY" "$LASTCHAR")
    else
        for ((i=0; i < ${#COMPREPLY[@]}; i++)); do
            [ -d "${COMPREPLY[$i]}" ] && COMPREPLY[$i]=${COMPREPLY[$i]}/
        done
    fi

    return 0
}

complete -o nospace -F _xyz xyz

1 Das Newline-Zeichen ist hier die offensichtliche Ausnahme, da es sich um ein internes Dateitrennzeichen handelt.

Dennis
quelle
Das funktioniert großartig (obwohl es schade ist, dass es nicht eingebaut ist). Vielen Dank!
Rob I
1
Gibt es einen Grund, "$ 2" anstelle von "$ {COMP_WORDS [COMP_CWORD]}" nicht zu verwenden?
Edward Falk
3

Ich denke, diese einfache Lösung funktioniert darin:

  1. Stimmt mit Verzeichnissen und Dateien überein, die auf enden .txt
  2. Behandelt Leerzeichen in den Dateinamen
  3. Fügt am Ende von Ordnerabschlüssen einen Schrägstrich ohne Leerzeichen ein
  4. Fügt am Ende einer Dateivervollständigungsübereinstimmung Speicherplatz hinzu

Der Schlüssel ging -o filenameszu Ende. Dies wurde mit GNU Bash 3.2.25 auf RHEL 5.3 und GNU Bash 4.3.18 auf OSX getestet

_xyz()
{
  local cur=${COMP_WORDS[COMP_CWORD]}

  local IFS=$'\n'
  COMPREPLY=( $( compgen -o plusdirs  -f -X '!*.txt' -- $cur ) )
}

complete -o filenames -F _xyz xyz
Chad Skeeters
quelle
Ja, das scheint großartig zu funktionieren. Viel einfacher, danke, dass Sie das wichtige Argument verstanden haben!
Rob ich