전역 스키마를 확장하는 가장 좋은 방법은 무엇입니까?

전역 스키마를 확장하는 가장 좋은 방법은 무엇입니까?

../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 nullglobPOSIX에 정의되지 않은 방식으로 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.)

관련 정보