Benutzerdefinierte automatische Vervollständigung: Behandeln Sie Leerzeichen in Dateinamen

8

Ein bisschen Kontext, diese Frage ist eine Fortsetzung dieser Frage: Bash Remote Autocompletion: Ändern Sie das 'Start'-Verzeichnis

Wie auch immer, ich schreibe mein benutzerdefiniertes Bash-Skript für die automatische Vervollständigung: Ich möchte, dass die automatische Vervollständigung genauso cdfunktioniert wie sie, außer dass ich die Namen aus einem bestimmten Verzeichnis abrufen möchte, nicht unbedingt aus dem aktuellen. Es funktioniert hervorragend, außer wenn Dateinamen Leerzeichen enthalten.

Ein kurzes Beispiel. Angenommen, das Verzeichnis, aus dem ich die Namen erhalte, enthält zwei Dateien: a_fileund another file(beachten Sie das Leerzeichen). Das passiert:

my_commandTABTAB
a_file file another

Die Präsentation ist nicht perfekt, aber die Idee ist, dass ich mit 3 Auswahlmöglichkeiten aufgefordert werde, another fileaufgeteilt in anotherund file. Die gewünschte Ausgabe wäre : file_1 another file. Ich möchte auch, dass Leerzeichen automatisch maskiert werden:

my_command anoTAB
my_command another\ file

So sieht mein Skript aus:

#! / bin / bash

_get_file_list ()
{
    dir = "/ some / path /"
    cd $ dir
    find * -maxdepth 0
}}

_GetOptMyCommand ()
{
    lokale cur

    COMPREPLY = ()
    cur = $ {COMP_WORDS [COMP_CWORD]}

    Fall "$ cur" in
    - *)
        COMPREPLY = ($ (compgen -W "-h -l --help --list -" - "$ cur")) ;;
    *)
        COMPREPLY = ($ (compgen -W "$ (_ get_file_list)" - "$ cur")) ;;
    esac

    0 zurückgeben
}}

complete -F _GetOptMyCommand my_command

Wie gehe ich mit Leerzeichen in Dateinamen um und mache mein Autocompletion-Skript so cd?

Pierre Mourlanne
quelle
1
Per Matthews Antwort unten local IFS=$'\n'scheint das zu sein, wonach Sie suchen. Das hat bei mir funktioniert.
Edward Falk

Antworten:

4

Glauben Sie, es könnte besser sein, compgenals findin diesem Fall zu verwenden.

Sie haben wahrscheinlich bereits ein Abschlussskript mit System. Versuchen Sie zB

locate bash_completion

Bei Debian-Varianten ist dies wahrscheinlich:

/usr/share/bash-completion/bash_completion

wo Sie zB finden _filedir. Der einfachste Weg wäre also etwas in Richtung:

*)
    pushd "/some/path" >/dev/null
    _filedir
    popd >/dev/null

Wenn dies keine Option ist, könnte dies ein Starter sein:

_comp_by_path()
{
    local opt cur dir
    local IFS=$'\n' x tmp
    local -a tokens

    opt="$1"
    cur="$2"
    dir="$3"

    # Enter target directory
    pushd "$dir" >/dev/null

    # Get directories, filtered against current
    [[ "$opt" != "-f" ]] && \
    x=$( compgen -d -- "$cur" ) &&
    while read -r tmp; do
        tokens+=( "$tmp" )
    done <<< "$x"

    # Get files, filtered against current
    [[ "$opt" != "-d" ]] && \
    x=$( compgen -f -- "$cur" ) &&
    while read -r tmp; do
        tokens+=( "$tmp" )
    done <<< "$x"

    # If anything found
    if [[ ${#tokens[@]} -ne 0 ]]; then
        # Make sure escaping is OK
        compopt -o filenames 2>/dev/null
        COMPREPLY+=( "${tokens[@]}" )
    fi

    # Go back
    popd >/dev/null
}

_GetOptMyCommand()
{
    local cur

    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"

    case "$cur" in
    -*)
        COMPREPLY=( $( compgen -W "-h -l --help --list --" -- "$cur" ) );;
    *)
        _comp_by_path "any" "$cur" "/some/path"
    esac
}

complete -F _GetOptMyCommand my_command

Eine Variante mit findkönnte etwas in diese Richtung sein:

_zaso()
{
    local dir="$1"

    pushd "$dir" >/dev/null
    find * -maxdepth 0 2>/dev/null
    popd >/dev/null
}

_comp_with_find()
{
    local cur dir      
    local IFS=$'\n'

    cur="$1"
    dir="$2"

    compopt -o filenames 2>/dev/null
    COMPREPLY=( $( compgen -W "$(_zaso "$dir")" -- "$cur" ) );
}

Beachten Sie auch, dass printfin Bash eine %qOption hat. Um Zeichenfolgen in Anführungszeichen zu generieren, können Sie mit folgenden Optionen spielen:

find * -maxdepth 0 2>/dev/null && \
while read -r tmp; do
    printf "%q\n" "$tmp"
done <<< "$x"

Auch nicht, dass Dateinamen Zeilenumbrüche enthalten können, in denen ein Großteil davon unterbrochen wird. Haben Sie nicht einen Weg zu benutzen gefunden \0mit compgen.

Runium
quelle
Vielen Dank für die Position der integrierten Autovervollständigung. Ich habe mich tatsächlich gefragt, wo sie gespeichert ist. Sobald ich meine Sachen zum Laufen gebracht habe, werde ich die Details veröffentlichen, nochmals vielen Dank!
Pierre Mourlanne
@PierreMourlanne: Ja, es gibt viel zu lernen, wenn man diese Skripte liest. Suchen Sie auch im (wahrscheinlich completions/) Unterverzeichnis, in dem Sie sich bash_completionbefinden, nach befehlsspezifischen Abschlussskripten. Es sollte eine ziemlich lange nette Liste mit Skripten geben.
Runium
Ebenfalls. Daran habe ich bei Ihrer vorherigen Frage nicht gedacht, compgenkann aber auch auf Ihrem SSH-Server verwendet werden.
Runium
3

Ich bin kürzlich auf dasselbe Problem gestoßen. Ich konnte die Dinge richtig funktionieren lassen, indem ich die IFS-Variable mit local IFS=$'\n'und dann mit Arrays änderte . Versuchen Sie Folgendes:

_GetOptMyCommand(){
    # Backup old nullglob setting
    local shoptbakup="`shopt -p nullglob`"
    shopt -s nullglob

    local cur opts i opt
    local IFS=$'\n'
    cur="${COMP_WORDS[COMP_CWORD]}"

    case "$cur" in
    -*)
        opts=("-h" "-l" "--help" "--list");;
    *)
        # Get Files
        opts=(${cur}*)
    esac

    COMPREPLY=( $( compgen -W "${opts[*]}" -- "$cur" ) )

    # Restore nullglob setting
    eval "$shoptbakup" 2>/dev/null

    return 0
}
complete -o filenames -o bashdefault -F _GetOptMyCommand my_command
Matthew
quelle
0

Versuchen Sie, den findAusgang durchzuleiten sed 's/ /\\ /'.

Beachten Sie jedoch, dass andere Zeichen (Anführungszeichen, $, & .. alle üblichen Verdächtigen) Sie ebenfalls stolpern können.

sed 's/\([ $&!#*()<>|{}[?`"'"'"']\)/\\\1/'

kann besser sein, da es den meisten "Sonderzeichen" entgeht.

(Ich habe jedoch noch nicht herausgefunden, wie ich richtig entkommen kann)

Kampu
quelle
Ich habe diese Art von Dingen bereits ausprobiert und die Ausgabe von find wird mit dem maskierten Leerzeichen ( another\ file) richtig formatiert . Das Problem liegt wahrscheinlich danach, compgen -W "$(_get_file_list)"weil ich immer noch mit den drei Auswahlmöglichkeiten aufgefordert werde file, anotherund a_file.
Pierre Mourlanne
Ich habe dieses Problem verstanden, wusste aber nicht, wie ich es beheben sollte. Sie benötigen eine Erweiterung im "$ @" - Stil. Ich weiß nur nicht, wie ich eine Erweiterung im "$ @" - Stil bekommen soll.
Kampu