Tự động hoàn thành tùy chỉnh: xử lý khoảng trắng trong tên tệp


8

Một chút bối cảnh, câu hỏi này là phần tiếp theo của câu hỏi này: Tự động hoàn thành từ xa Bash: thay đổi thư mục 'bắt đầu'

Dù sao, tôi đang viết kịch bản bash tự động hoàn thành tùy chỉnh của mình: Tôi muốn tự động hoàn thành hoạt động giống như cdvậy, ngoại trừ việc tôi muốn lấy tên từ một thư mục cụ thể, không nhất thiết phải là tên hiện tại. Nó hoạt động tuyệt vời trừ khi tên tập tin có không gian trong đó.

Một ví dụ nhanh. Giả sử thư mục tôi nhận được tên từ đó có hai tệp trong đó: a_fileanother file(chú ý khoảng trắng). Điều này xảy ra:

my_commandTABTAB
a_file file another

Bài thuyết trình không hoàn hảo nhưng ý tưởng là tôi được nhắc với 3 lựa chọn, another fileđược chia thành anotherfile. Đầu ra mong muốn sẽ là : file_1 another file. Tôi cũng muốn các không gian được thoát tự động:

my_command anoTAB
my_command another\ file

Đây là kịch bản của tôi.

#! / bin / bash

_get_file_list ()
{
    dir = "/ một số / đường dẫn /"
    cd $ dir
    tìm * -maxdepth 0
}

_GetOptMyCommand ()
{
    cur địa phương

    TƯƠNG THÍCH = ()
    cur = $ {COMP_WORDS [COMP_CWORD]}

    trường hợp "$ cur" trong
    - *)
        COMPREPLY = ($ (compgen -W "-h -l --help --list -" - "$ cur")) ;;
    *)
        COMPREPLY = ($ (compgen -W "$ (_ get_file_list)" - "$ cur")) ;;
    esac

    trả về 0
}

hoàn thành -F _GetOptMyCommand my_command

Làm cách nào để xử lý các khoảng trắng trong tên tệp và tạo tập lệnh tự động hoàn thành của tôi như thế cdnào?


1
Câu trả lời của Matthew dưới đây, local IFS=$'\n'dường như là những gì bạn đang tìm kiếm. Đó là những gì làm việc cho tôi.
Edward Falk

Câu trả lời:


4

Tin rằng nó có thể tốt hơn để sử dụng compgenthay vì findtrong trường hợp này.

Bạn có thể đã có một kịch bản hoàn thành với hệ thống. Hãy thử ví dụ

locate bash_completion

Trên các biến thể Debian, điều này có thể là:

/usr/share/bash-completion/bash_completion

nơi bạn tìm thấy, ví dụ _filedir. Vì vậy, cách đơn giản nhất sau đó sẽ là một cái gì đó theo hướng:

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

Nếu đó không phải là một lựa chọn thì đây có thể là một khởi đầu:

_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

Một biến thể sử dụng findcó thể là một cái gì đó theo hướng này:

_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" ) );
}

Cũng lưu ý rằng printftrong Bash có một %qtùy chọn. Vì vậy, để tạo chuỗi trích dẫn, đây là một tùy chọn để chơi với:

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

Ngoài ra, không phải tên tệp có thể có các ký tự dòng mới, trong đó rất nhiều thứ này sẽ bị hỏng. Không tìm thấy một cách để sử dụng \0với compgen.


Cảm ơn vị trí của tự động hoàn thành tích hợp, tôi thực sự tự hỏi nơi nó được lưu trữ. Khi tôi làm việc xong, tôi sẽ đăng chi tiết, cảm ơn lần nữa!
Pierre Mourlanne

@PierreMourlanne: Vâng, có rất nhiều điều để học hỏi từ việc đọc các kịch bản đó. Cũng xem trong completions/thư mục con (có thể ) nơi bash_completioncư trú của các tập lệnh hoàn thành cụ thể lệnh. Nên có một danh sách khá dài với các kịch bản.
Runium

Cũng thế. Tôi không nghĩ về câu hỏi trước của bạn, nhưng compgencũng có thể được sử dụng trên máy chủ ssh của bạn.
Runium

3

Tôi đã gặp vấn đề tương tự gần đây. Tôi đã có thể làm cho mọi thứ hoạt động chính xác bằng cách thay đổi biến IFS local IFS=$'\n'và sau đó sử dụng mảng. Hãy thử sử dụng cái này:

_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

0

Hãy thử đường ống findđầu ra thông qua sed 's/ /\\ /'.

Tuy nhiên, xin lưu ý rằng các ký tự khác (dấu ngoặc kép, $, & .. tất cả các nghi phạm thông thường) cũng có thể khiến bạn gặp khó khăn.

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

có thể tốt hơn, vì nó sẽ thoát khỏi hầu hết các nhân vật "đặc biệt".

(Tuy nhiên, tôi vẫn chưa tìm ra cách thoát chính xác \)


Tôi đã thử loại điều này và đầu ra của tìm kiếm được định dạng đúng với không gian thoát ( another\ file). Vấn đề có lẽ nằm ở sau đó, compgen -W "$(_get_file_list)"bởi vì tôi vẫn được nhắc nhở với ba lựa chọn file, anothera_file.
Pierre Mourlanne

Tôi hiểu vấn đề đó nhưng không biết cách khắc phục. Bạn cần mở rộng kiểu "$ @". Tôi chỉ không biết làm thế nào để có được "$ @" mở rộng phong cách.
kampu
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.