[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
에서 가져온 아이디어