[bash] bash 루프 목록에서 공백을 어떻게 이스케이프 할 수 있습니까?
특정 디렉토리의 모든 하위 디렉토리 (파일 제외)를 반복하는 bash 쉘 스크립트가 있습니다. 문제는 일부 디렉토리 이름에 공백이 있다는 것입니다.
내 테스트 디렉토리의 내용은 다음과 같습니다.
$ls -F test
Baltimore/ Cherry Hill/ Edison/ New York City/ Philadelphia/ cities.txt
그리고 디렉토리를 반복하는 코드 :
for f in `find test/* -type d`; do
echo $f
done
출력은 다음과 같습니다.
테스트 / 볼티모어 테스트 / 체리 언덕 테스트 / 에디슨 테스트 / 신규 요크 시티 test / 필라델피아
Cherry Hill과 New York City는 2 ~ 3 개의 개별 항목으로 처리됩니다.
다음과 같이 파일 이름을 인용 해 보았습니다.
for f in `find test/* -type d | sed -e 's/^/\"/' | sed -e 's/$/\"/'`; do
echo $f
done
그러나 소용이 없습니다.
이것을하기위한 간단한 방법이 있어야합니다.
아래 답변은 훌륭합니다. 그러나 이것을 더 복잡하게 만들기 위해-나는 항상 내 테스트 디렉토리에 나열된 디렉토리를 사용하고 싶지는 않습니다. 때로는 디렉토리 이름을 명령 줄 매개 변수로 대신 전달하고 싶습니다.
IFS 설정에 대한 Charles의 제안을 받아 들여 다음을 생각해 냈습니다.
dirlist="${@}"
(
[[ -z "$dirlist" ]] && dirlist=`find test -mindepth 1 -type d` && IFS=$'\n'
for d in $dirlist; do
echo $d
done
)
그리고 이것은 명령 줄 인수에 공백이 없으면 잘 작동합니다 (해당 인수가 인용 된 경우에도). 예를 들어 test.sh "Cherry Hill" "New York City"
다음과 같은 스크립트를 호출 하면 다음과 같은 출력 이 생성됩니다.
체리 언덕 새로운 요크 시티
답변
첫째, 그렇게하지 마십시오. 가장 좋은 방법은 find -exec
올바르게 사용하는 것입니다.
# this is safe
find test -type d -exec echo '{}' +
다른 안전한 접근 방식은 NUL로 끝나는 목록을 사용하는 것입니다 -print0
.
# this is safe
while IFS= read -r -d '' n; do
printf '%q\n' "$n"
done < <(find test -mindepth 1 -type d -print0)
find에서 배열을 채우고 나중에 해당 배열을 전달할 수도 있습니다.
# this is safe
declare -a myarray
while IFS= read -r -d '' n; do
myarray+=( "$n" )
done < <(find test -mindepth 1 -type d -print0)
printf '%q\n' "${myarray[@]}" # printf is an example; use it however you want
찾기가를 지원하지 않으면 -print0
결과는 안전하지 않습니다. 파일 이름에 줄 바꿈이 포함 된 파일이 있으면 아래 내용이 원하는대로 작동하지 않습니다 (예, 합법적 임).
# this is unsafe
while IFS= read -r n; do
printf '%q\n' "$n"
done < <(find test -mindepth 1 -type d)
위의 방법 중 하나를 사용하지 않을 경우 세 번째 방법 (워드 분할을 수행하기 전에 하위 프로세스의 전체 출력을 읽으므로 시간과 메모리 사용 측면에서 덜 효율적입니다)은 다음과 같은 IFS
변수 를 사용하는 것 입니다. 공백 문자를 포함하지 않습니다. , 또는 set -f
과 같은 glob 문자를 포함하는 문자열 이 확장 되지 않도록하려면 globbing ( )을 끄십시오 .[]
*
?
# this is unsafe (but less unsafe than it would be without the following precautions)
(
IFS=$'\n' # split only on newlines
set -f # disable globbing
for n in $(find test -mindepth 1 -type d); do
printf '%q\n' "$n"
done
)
마지막으로, 명령 줄 매개 변수의 경우 쉘에서 지원하는 경우 배열을 사용해야합니다 (예 : ksh, bash 또는 zsh).
# this is safe
for d in "$@"; do
printf '%s\n' "$d"
done
분리를 유지할 것입니다. 인용 (및 $@
대신 사용 $*
)이 중요합니다. 배열은 glob 표현식과 같은 다른 방법으로도 채울 수 있습니다.
# this is safe
entries=( test/* )
for d in "${entries[@]}"; do
printf '%s\n' "$d"
done
답변
find . -type d | while read file; do echo $file; done
그러나 파일 이름에 줄 바꿈이 포함되어 있으면 작동하지 않습니다. 위는 실제로 변수에 디렉토리 이름을 원할 때 내가 아는 유일한 솔루션입니다. 일부 명령을 실행하려면 xargs를 사용하십시오.
find . -type d -print0 | xargs -0 echo 'The directory is: '
답변
다음은 파일 이름의 탭 및 / 또는 공백을 처리하는 간단한 솔루션입니다. 줄 바꿈과 같은 파일 이름의 다른 이상한 문자를 처리해야하는 경우 다른 답을 선택하십시오.
테스트 디렉토리
ls -F test
Baltimore/ Cherry Hill/ Edison/ New York City/ Philadelphia/ cities.txt
디렉토리로 이동하는 코드
find test -type d | while read f ; do
echo "$f"
done
"$f"
인수로 사용되는 경우 파일 이름은 따옴표 ( ) 로 묶어야합니다 . 따옴표가 없으면 공백이 인수 구분 기호로 작동하고 호출 된 명령에 여러 인수가 제공됩니다.
그리고 출력 :
test/Baltimore
test/Cherry Hill
test/Edison
test/New York City
test/Philadelphia
답변
이것은 표준 유닉스에서 매우 까다 롭고 대부분의 솔루션은 줄 바꿈이나 다른 문자를 잘못 실행합니다. 그러나 GNU 도구 세트를 사용하는 경우 find
옵션 -print0
을 활용 xargs
하고 해당 옵션 -0
(-0) 과 함께 사용할 수 있습니다 . 단순한 파일 이름에 나타날 수없는 두 문자가 있습니다. 슬래시와 NUL ‘\ 0’입니다. 분명히 슬래시가 경로명에 나타나기 때문에 NUL ‘\ 0’을 사용하여 이름의 끝을 표시하는 GNU 솔루션은 독창적이고 완벽합니다.
답변
그냥 두지 않는 이유
IFS='\n'
for 명령 앞에? 이렇게하면 필드 구분 기호가 <Space> <Tab> <Newline>에서 <Newline>으로 변경됩니다.
답변
find . -print0|while read -d $'\0' file; do echo "$file"; done
답변
나는 사용한다
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
for f in $( find "$1" -type d ! -path "$1" )
do
echo $f
done
IFS=$SAVEIFS
충분하지 않습니까? http://www.cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html
에서 가져온 아이디어