와일드카드 확장에서 첫 번째 일치 항목을 얻는 방법은 무엇입니까?

와일드카드 확장에서 첫 번째 일치 항목을 얻는 방법은 무엇입니까?

Bash 및 Zsh와 같은 셸은 패턴과 일치하는 인수 수를 사용하여 와일드카드를 인수로 확장합니다.

$ echo *.txt
1.txt 2.txt 3.txt

하지만 모든 일치 항목이 아닌 첫 번째 일치 항목만 반환하고 싶다면 어떻게 해야 할까요?

$ echo *.txt
1.txt

쉘 특정 솔루션은 마음에 들지 않지만 파일 이름의 공백을 처리할 수 있는 솔루션을 원합니다.

답변1

Bash에서 안정적인 방법은 배열로 확장하여 첫 번째 요소만 출력하는 것입니다.

pattern="*.txt"
files=( $pattern )
echo "${files[0]}"  # printf is safer!

echo $files( 누락된 인덱스를 [0]으로 처리 할 수도 있습니다 .)

이는 파일 이름을 확장할 때 공백/탭/줄 바꿈 및 기타 메타 문자를 안전하게 처리합니다. 유효한 로케일 설정에 따라 "첫 번째"의 의미가 변경될 수 있습니다.

bash를 사용하여 대화식으로 이 작업을 수행할 수도 있습니다.완전한 기능:

_echo() {
    local cur=${COMP_WORDS[COMP_CWORD]}   # string to expand

    if compgen -G "$cur*" > /dev/null; then
        local files=( ${cur:+$cur*} )   # don't expand empty input as *
        [ ${#files} -ge 1 ] && COMPREPLY=( "${files[0]}" )
    fi
}
complete -o bashdefault -F _echo echo

이는 _echo함수를 echo명령의 전체 인수에 바인딩합니다(정상 완성을 재정의함). 위의 코드에는 추가 "*"가 추가되어 있으며 부분적인 파일 이름에서 탭을 누르면 올바른 일이 일어나기를 바랍니다.

코드는약간설명할 수 없지만 ()를 설정하거나 가정하는 대신 nullglobglob을 일부 일치하도록 확장한 다음 안전하게 배열로 확장하고 마지막으로 참조가 신뢰할 수 있도록 COMPREPLY를 설정할 수 있는지 확인합니다.shopt -s nullglobcompgen -G

당신은 할 수부분적으로bash 를 사용하여 이 작업을 수행할 수 있지만(프로그래밍 방식으로 전역 확장) compgen -G표준 출력에 따옴표를 사용하지 않고 출력하기 때문에 강력하지 않습니다.

평소와 같이 완료는 매우 걱정스러운 일이며 이로 인해 환경 변수를 포함한 다른 것의 완료가 중단됩니다( _bash_def_completion()함수 참조).여기기본 동작 시뮬레이션에 대한 자세한 내용은 참조).

compgen완성 기능 외부에서도 사용할 수 있습니다 .

files=( $(compgen -W "$pattern") )

한 가지 주목해야 할 점은 "~"는 glob이 아니며 $variables 및 기타 확장과 마찬가지로 별도의 확장 단계에서 bash에 의해 처리된다는 것입니다. compgen -G파일 이름 글로빙만 수행하지만 compgen -Wbash의 기본 확장자를 모두 제공합니다( ``및 포함 $()). -G같지 않은-W 안전한 인용문(차이점을 설명할 수 없음) 그 목적 -W은 태그를 확장하는 것이므로 해당 파일이 존재하지 않더라도 "a"를 "a"로 확장한다는 의미이므로 아마도 이상적이지 않을 것입니다.

이는 이해하기 쉽지만 원치 않는 부작용이 있을 수 있습니다.

_echo() {
    local cur=${COMP_WORDS[COMP_CWORD]}
    local files=( $(compgen -W "$cur") ) 
    printf -v COMPREPLY %q "${files[0]}"  
}

그 다음에:

touch $'curious \n filename'

echo curious*tab

이러한 값을 안전하게 참조 하려면 주의해서 사용하세요 printf %q.

마지막 옵션은 GNU 유틸리티에서 0으로 구분된 출력을 사용하는 것입니다(참조:배쉬 FAQ):

pattern="*.txt"
while IFS= read -r -d $'\0' filename; do 
    printf '%q' "$filename"; 
    break; 
done < <(find . -maxdepth 1 -name "$pattern" -printf "%f\0" | sort -z )

이 옵션을 사용하면 정렬 순서를 더 잘 제어할 수 있지만(전역을 확장할 때 순서는 로케일에 따라 다르며 대/소문자를 LC_COLLATE접을 수도 있고 안 접을 수도 있음), 이렇게 작은 문제의 경우 꽤 큰 망치입니다 ;-)

답변2

zsh에서는 다음을 사용하십시오.[1] 글로벌 예선. 이 특별한 경우가 최대 하나의 일치 항목을 반환하더라도 여전히 목록이며 단일 단어(예: 할당)가 필요한 컨텍스트에서는 전역 변수가 확장되지 않습니다(배열 할당 제외).

echo *.txt([1])

ksh 또는 bash에서는 전체 일치 목록을 배열로 채우고 첫 번째 요소를 사용할 수 있습니다.

tmp=(*.txt)
echo "${tmp[0]}"

모든 셸에서 위치 매개변수를 설정하고 첫 번째 매개변수를 사용할 수 있습니다.

set -- *.txt
echo "$1"

이로 인해 위치 매개변수가 중단됩니다. 이를 원하지 않으면 서브쉘을 사용할 수 있습니다.

echo "$(set -- *.txt; echo "$1")"

자체 위치 매개변수 세트가 있는 함수를 사용할 수도 있습니다.

set_to_first () {
  eval "$1=\"\$2\""
}
set_to_first f *.txt
echo "$f"

답변3

노력하다:

for i in *.txt; do printf '%s\n' "$i"; break; done
1.txt

파일 이름 확장자는 현재 로케일에 적용되는 조합 순서에 따라 정렬됩니다.

답변4

간단한 해결책:

sh -c 'echo "$1"' sh *.txt

printf또는 원하는 경우 이를 사용하십시오.

관련 정보