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
명령의 전체 인수에 바인딩합니다(정상 완성을 재정의함). 위의 코드에는 추가 "*"가 추가되어 있으며 부분적인 파일 이름에서 탭을 누르면 올바른 일이 일어나기를 바랍니다.
코드는약간설명할 수 없지만 ()를 설정하거나 가정하는 대신 nullglob
glob을 일부 일치하도록 확장한 다음 안전하게 배열로 확장하고 마지막으로 참조가 신뢰할 수 있도록 COMPREPLY를 설정할 수 있는지 확인합니다.shopt -s nullglob
compgen -G
당신은 할 수부분적으로bash 를 사용하여 이 작업을 수행할 수 있지만(프로그래밍 방식으로 전역 확장) compgen -G
표준 출력에 따옴표를 사용하지 않고 출력하기 때문에 강력하지 않습니다.
평소와 같이 완료는 매우 걱정스러운 일이며 이로 인해 환경 변수를 포함한 다른 것의 완료가 중단됩니다( _bash_def_completion()
함수 참조).여기기본 동작 시뮬레이션에 대한 자세한 내용은 참조).
compgen
완성 기능 외부에서도 사용할 수 있습니다 .
files=( $(compgen -W "$pattern") )
한 가지 주목해야 할 점은 "~"는 glob이 아니며 $variables 및 기타 확장과 마찬가지로 별도의 확장 단계에서 bash에 의해 처리된다는 것입니다. compgen -G
파일 이름 글로빙만 수행하지만 compgen -W
bash의 기본 확장자를 모두 제공합니다( ``
및 포함 $()
). -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
또는 원하는 경우 이를 사용하십시오.