사용자 정의 자동 완성: 파일 이름의 공백 처리

사용자 정의 자동 완성: 파일 이름의 공백 처리

약간의 배경 정보입니다. 이 질문은 이에 대한 후속 조치입니다.Bash 원격 자동 완성: "시작" 디렉터리 변경

어쨌든, 나는 사용자 정의 자동 완성 bash 스크립트를 작성하고 있습니다. cd특정 디렉터리(반드시 현재 디렉터리일 필요는 없음)에서 이름을 가져오고 싶다는 점을 제외하면 자동 완성 기능이 이전처럼 작동하길 원합니다. 파일 이름에 공백이 포함되어 있지 않으면 제대로 작동합니다.

간단한 예입니다. 이름을 가져오는 디렉토리에 두 개의 파일이 있다고 가정해 보겠습니다. a_fileanother file(공백에 유의하세요). 때로는 다음과 같이 진행됩니다.

my_commandTABTAB
a_file file another

프레젠테이션이 완벽하지는 않지만 과 another file로 나누어진 3가지 선택 사항을 제시하는 것이 좋습니다 . 원하는 출력은 다음과 같습니다 . 또한 공백이 자동으로 이스케이프되기를 원합니다.anotherfilefile_1 another file

my_command anoTAB
my_command another\ file

내 스크립트는 다음과 같습니다

#!/bin/bash

_get_file_list()
{
    dir="/일부/경로/"
    CD $디렉토리
    * 찾기 - 최대 깊이 0
}

_GetOptMyCommand()
{
    지역 전류

    완료=()
    현재=${COMP_WORDS[COMP_CWORD]}

    케이스 "$cur"
    -*)
        COMPREPLY=( $( compgen -W "-h -l --help --list --" -- "$cur" ) );;
    *)
        COMPREPLY=( $( compgen -W "$(_get_file_list)" -- "$cur" ) );;
    이삭

    0을 반환
}

전체 -F _GetOptMyCommand my_command

파일 이름의 공백을 어떻게 처리하고 자동 완성 스크립트를 다음과 같이 만들 수 있습니까 cd?

답변1

이 경우 compgen에는 이를 사용하는 것이 더 나을 수도 있습니다 .find

이미 시스템 완료 스크립트가 있을 수 있습니다. 예를 들어 시도해보십시오.

locate bash_completion

데비안 변형에서는 다음과 같을 수 있습니다:

/usr/share/bash-completion/bash_completion

예를 들어 어디서 찾을 수 있습니까 _filedir? 가장 간단한 방법은 다음과 같습니다.

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

이것이 옵션이 아닌 경우 다음과 같이 시작하는 방법이 될 수 있습니다.

_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

사용된 변형은 find다음과 같습니다.

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

또한 printfBash에는 옵션이 있습니다 %q. 따라서 인용된 문자열을 생성하려면 다음 옵션을 사용할 수 있습니다.

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

또한 파일 이름에는 개행 문자를 포함할 수 없으며 그 중 대부분이 손상될 수 있습니다. 아직 \0with 을 사용할 방법을 찾지 못했습니다 compgen.

답변2

최근에 같은 문제가 발생했습니다. local IFS=$'\n'IFS 변수를 변경한 다음 배열을 사용하여 작업을 수행 할 수 있었습니다 . 이것을 사용해 보세요:

_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

답변3

find출력을 파이핑 해 보십시오 sed 's/ /\\ /'.

하지만 다른 문자(따옴표, $, &...모든 일반적인 문자)도 문제를 일으킬 수 있다는 점에 유의하세요.

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

대부분의 "특수" 문자를 이스케이프 처리하므로 더 나을 수도 있습니다.

(그래도 \를 올바르게 이스케이프 처리하는 방법을 찾지 못했습니다.)

관련 정보