이를 달성하기위한 몇 가지 실행 가능한 방법이 있습니다.
원래 버전에 가깝게 고정하려면 다음과 같이하십시오.
getlist() {
IFS=$'\n'
for file in $(find . -iname 'foo*') ; do
printf 'File found: %s\n' "$file"
done
}
파일 이름에 리터럴 개행 문자가 있으면 여전히 실패하지만 공백은 끊지 않습니다.
그러나 IFS를 망칠 필요는 없습니다. 여기에 내가 선호하는 방법이 있습니다.
getlist() {
while IFS= read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
< <(command)
익숙하지 않은 구문 을 찾으면 프로세스 대체 에 대해 읽어야 합니다. 이 방법의 장점은 for file in $(find ...)
공백, 줄 바꿈 및 기타 문자가 포함 된 파일이 올바르게 처리된다는 것입니다. 때문에이 작품 find
과 -print0
용도 것 null
(일명를 \0
줄 바꿈과는 달리, 각 파일 이름에 대한 터미네이터로)과, 널 (null) 파일 이름에 법적 문자가 아닙니다.
거의 동등한 버전에 비해 이점
getlist() {
find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
printf 'File found: %s\n' "$file"
done
}
while 루프의 본문에 변수 할당이 유지된다는 것입니다. 즉, while
위와 같이 파이프 하면 몸체가 while
서브 쉘에 있으며 원하는 것이 아닐 수도 있습니다.
프로세스 대체 버전의 장점 find ... -print0 | xargs -0
은 최소입니다. xargs
파일을 한 줄로 인쇄하거나 단일 작업을 수행하는 것만으로도 버전이 양호하지만 여러 단계를 수행해야하는 경우 루프 버전이 더 쉽습니다.
편집 : 여기 에이 문제를 해결하기위한 다른 시도의 차이점에 대한 아이디어를 얻을 수있는 좋은 테스트 스크립트가 있습니다.
#!/usr/bin/env bash
dir=/tmp/getlist.test/
mkdir -p "$dir"
cd "$dir"
touch 'file not starting foo' foo foobar barfoo 'foo with spaces'\
'foo with'$'\n'newline 'foo with trailing whitespace '
# while with process substitution, null terminated, empty IFS
getlist0() {
while IFS= read -d $'\0' -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
# while with process substitution, null terminated, default IFS
getlist1() {
while read -d $'\0' -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done < <(find . -iname 'foo*' -print0)
}
# pipe to while, newline terminated
getlist2() {
find . -iname 'foo*' | while read -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# pipe to while, null terminated
getlist3() {
find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# for loop over subshell results, newline terminated, default IFS
getlist4() {
for file in "$(find . -iname 'foo*')" ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# for loop over subshell results, newline terminated, newline IFS
getlist5() {
IFS=$'\n'
for file in $(find . -iname 'foo*') ; do
printf 'File found: '"'%s'"'\n' "$file"
done
}
# see how they run
for n in {0..5} ; do
printf '\n\ngetlist%d:\n' $n
eval getlist$n
done
rm -rf "$dir"