../smth*/*
프로그래밍 방식으로 전역 패턴(예 : 또는 /etc/cron*/
)을 파일 목록으로 확장해야 합니다 . 가장 좋은 접근 방식은 무엇입니까?
답변1
배열 선언의 오른쪽으로 확장되도록 하세요.
list=(../smth*/) # grab the list
echo "${#list[@]}" # print array length
echo "${list[@]}" # print array elements
for file in "${list[@]}"; do echo "$file"; done # loop over the array
nullglob
쉘 옵션을 설정해야 합니다.
기본적으로 설정되지 않습니다.zsh
오류를 발생시키거나( 또는 에서 bash -O failglob
) 문자 그대로 전달되는 대신(다른 모든 Bourne 유사 쉘) 일치하지 않는 glob이 null로 확장됩니다 .
bash
으로 설정하다
shopt -s nullglob
또는 함께 zsh
또는 yash
함께
set -o nullglob
zsh
( nullglob
원래 출처) 전역 설정을 변경하지 않으려면 glob 한정자를 사용하는 것이 좋습니다 (N)
.
list( ../smth*/(N) )
ksh93에 해당:
list=( ~(N)../smth*/ )
답변2
compgen
이스케이프(!) 패턴을 전달할 수 있는 Bash 내장 기능이며 일치 항목이 존재하는지 여부에 따라 true 또는 false를 반환하여 출력합니다. 이는 변수/스크립트 인수에서 전역 패턴을 전달해야 하는 경우 특히 유용합니다.
glob_pattern='../smth*/*'
while read -r file; do
# your thing
echo "read $file"
done < <(compgen -G "$glob_pattern" || true)
|| true
오류 반환으로 인해 compgen
문제가 발생하는 것을 방지하기 위해 추가되었습니다 . 이 방법은 일치하지 않는 문제를 방지하고 nullglob 옵션을 변경할 필요가 없습니다.
배열의 항목이 필요한 경우 files=()
루프 앞과 files+=("$file")
루프 내부에서 항목을 초기화하면 됩니다. 그런 다음 간단히 를 사용하여 배열의 길이를 확인하여 일치하는 항목이 있는지 확인할 수 있습니다 if [[ ${#files[@]} -gt 0 ]]; then
.
답변3
생성된 명령이 명령줄 길이 제한을 초과하는 경우 표준 입력(파이프라인)을 사용하고 싶습니다. 다음 명령이 나에게 효과적이었습니다.
echo "../smth*/*" "/etc/cron*/" | xargs -n1 -I{} bash -O nullglob -c "echo {}" | xargs -n1
또는 전역 목록의 경우:
cat huge_glob_list.txt | xargs -n1 -I{} bash -O nullglob -c "echo {}" | xargs -n1
답변4
나는 최근에 같은 문제를 겪었습니다. 나는 해결책이 매우 간단하다는 것을 알았습니다. (그리고 POSIX와 호환됩니다.)
$IFS
공백 문자로 단어 분할을 비활성화하려면 빈 문자열로 설정합니다 .- 그런 다음 변수를 역참조하여 전역적으로 확장할 수 있습니다.
샘플 코드는 그림에 나와 있습니다.for 루프:
pattern='some * dir/my file *'
unset old_IFS ; [ -n "${IFS+x}" ] && old_IFS=${IFS} ; IFS=''
IFS=''
for f in ${pattern} ; do
IFS=${old_IFS} ; [ -z "${old_IFS+x}" ] && unset IFS
printf 'Filenames: %s \n' "${f}"
done
shopt -s nullglob
POSIX에 정의되지 않은 방식으로 nullglob을 설정 하지 않는다는 점에 유의하세요 . shopt
전역 패턴을 찾을 수 없으면 패턴이 자체적으로 확장됩니다. Filenames: some dir/my file *
위의 코드로 인쇄되었습니다. if [ -e "${f}" ]; then ...
필요한 경우 수표를 추가하는 것은 쉽습니다.
동일한 방법을 사용하여 설정할 수 있습니다.위치 매개변수반품.
pattern='some * dir/my file *'
unset old_IFS ; [ -n "${IFS+x}" ] && old_IFS=${IFS} ; IFS=''
set -- ${pattern}
IFS=${old_IFS} ; [ -z "${old_IFS+x}" ] && unset IFS
unset old_IFS
printf '[%s]\n' "$@"
이것을 한 줄로 바꿀 수는 없습니다 IFS='' command set -- ${pattern}
. 이 줄은 단어 분할을 비활성화하지 않습니다.
그것은 사용될 수 있습니다기능 매개변수, 그러나 이는 권장되지 않습니다. 복구는 $IFS
함수의 첫 번째 명령문에서 이루어져야 하며 이는 비대칭적이고 쉽게 잊혀집니다.
func() {
IFS=${old_IFS} ; [ -z "${old_IFS+x}" ] && unset IFS
unset old_IFS
printf '[%s]\n' "$@"
}
pattern='some * dir/my file *'
unset old_IFS ; [ -n "${IFS+x}" ] && old_IFS=${IFS} ; IFS=''
func $pattern
개인적으로 저는 $pattern
함수에 전달한 다음 set -- $pattern
함수 내부에 전달하는 것을 선호합니다. 그러나 함수가 다른 위치 매개변수도 사용하는 경우 이것이 항상 가능한 것은 아닙니다.
func() {
unset old_IFS ; [ -n "${IFS+x}" ] && old_IFS=${IFS} ; IFS=''
set -- ${pattern}
IFS=${old_IFS} ; [ -z "${old_IFS+x}" ] && unset IFS
unset old_IFS
printf '[%s]\n' "$@"
}
pattern='some * dir/my file *'
func $pattern
이 방법은 패턴 및 파일 경로에 사용됩니다.
- 공백 문자, 실제로는 모든 문자가 포함된 경우
\*
전역 문자가 포함된 경우 리터럴을 일치시키기 위해 이스케이프를 사용합니다*
.- glob 문자가 디렉터리 경로 구성 요소 및/또는 파일 이름 구성 요소에 있는 경우. (디렉토리 경로에 glob을 사용해야 한다면 쉽게 할 수 없습니다
find
.)