파일 이름에 공백이있을 때 find 명령의 출력을 어떻게 구문 분석합니까? 이름에 공백이 있으면 중단됩니다.

다음과 같은 루프 사용

for i in `find . -name \*.txt`

일부 파일 이름에 공백이 있으면 중단됩니다.

이 문제를 피하기 위해 어떤 기술을 사용할 수 있습니까?



답변

쉘 스크립트에서 파일 이름을 올바르게 구문 분석하는 것은 항상 어렵 기 때문에 이상적으로는 그렇게하지 않는 것이 이상적입니다 (공백으로 수정하면 다른 포함 문자, 특히 줄 바꿈에 여전히 문제가 있음). 이것은 심지어 BashPitfalls 페이지 의 첫 번째 항목 으로 표시됩니다 .

즉, 원하는 것을 거의 할 수있는 방법이 있습니다.

oIFS=$IFS
IFS=$'\n'

find . -name '*.txt' | while read -r i; do
  # use "$i" with whatever you're doing
done

IFS=$oIFS

$i나중에 공백을 해석하는 다른 것을 피하기 위해 사용할 때 인용해야합니다 . $IFS사용하지 않으면 나중에 오류가 발생할 수 있으므로 사용 후 다시 설정 해야합니다.

여기에는 또 하나의 경고가 첨부되어 있습니다. while루프 내에서 발생 하는 작업은 사용중인 정확한 쉘에 따라 서브 쉘에서 발생할 수 있으므로 변수 설정이 유지되지 않을 수 있습니다. for루프 버전은 이것을 피하지만 가격 $IFS에 공백 문제를 피하기 위해 솔루션을 적용하더라도 find너무 많은 파일을 반환 하면 문제가 발생할 수 있습니다.

어떤 시점 에서이 모든 것에 대한 올바른 수정은 쉘 대신 Perl 또는 Python과 같은 언어로 수행됩니다.


답변

find -print0그것을 사용 하여 파이프 xargs -0하거나 자신의 작은 C 프로그램을 작성하여 작은 C 프로그램으로 파이프하십시오. 이것은 무엇 -print0-0위해 고안된 것입니다.

셸 스크립트는 파일 이름에 공백이있는 파일 이름을 처리하는 가장 좋은 방법은 아닙니다. 할 수는 있지만 복잡합니다.


답변

“internal field separator”( IFS)를 루프 인수 분할을위한 공간 이외의 다른 것으로 설정할 수 있습니다.

ORIGIFS=${IFS}
NL='
'
IFS=${NL}
for i in $(find . -name '*.txt'); do
    IFS=${ORIGIFS}
    #do stuff
done
IFS=${ORIGIFS}

IFS찾기에서 사용한 후에 재설정 했는데, 대부분 멋져 보이기 때문입니다. 줄 바꿈으로 설정하는 데 아무런 문제가 없었지만 이것이 더 깨끗하다고 ​​생각합니다.

의 출력으로 수행하려는 작업에 따라 다른 방법 은 명령 과 함께 find직접 사용 하거나에 파이프하는 것 입니다. 첫 번째 경우 에는 파일 이름 이스케이프를 처리합니다. 이 경우 출력을 널 구분 기호로 인쇄 한 다음 분할합니다. 파일 이름에는 해당 문자 (내가 아는 것)를 포함 할 수 없으므로 항상 안전합니다. 이것은 간단한 경우에 주로 유용합니다. 일반적으로 전체 루프를 대체하지는 않습니다 .-execfind-print0xargs -0find-print0findxargsfor


답변

find -print0함께 사용xargs -0

find -print0와 함께 사용하면 xargs -0합법적 인 파일 이름에 대해 완전히 견고하며 사용 가능한 가장 확장 가능한 방법 중 하나입니다. 예를 들어, 현재 디렉토리 내의 모든 PDF 파일 목록을 원한다고 가정하십시오. 당신은 쓸 수 있습니다

$ find . -iname '*.pdf' -print0 | xargs -0 -n 1 echo

-iname '*.pdf'현재 디렉토리 ( .) 및 하위 디렉토리에서 모든 PDF (via )를 찾아서 각 인수를 echo명령 의 인수로 전달합니다 . -n 1옵션 을 지정 했으므로 xargs한 번에 하나의 인수 만에 전달됩니다 echo. 해당 옵션을 생략했다면 xargs가능한 한 많은 정보를 전달했을 것 echo입니다. echo short input | xargs --show-limits명령 행에 몇 바이트가 허용되는지 확인할 수 있습니다 .

무엇 않습니다 xargs정확히 무엇입니까?

보다 정확한 방식으로 인수를 반영하는 스크립트를 사용하여 xargs입력에 미치는 영향 -n, 특히 영향에 대한 효과를 명확하게 확인할 수 있습니다 echo.

$ cat > echoArgs.sh <<'EOF'
#!/bin/bash
echo "Number of arguments: $#"

[[ $# -eq 0 ]] && exit

for i in $(seq 1 $#); do
    echo "Arg $i: <$1>"
    shift
done
EOF

$ find . -iname '*.pdf' -print0 | xargs -0 ./echoArgs.sh
$ find . -iname '*.pdf' -print0 | xargs -0 -n 1 ./echoArgs.sh

공백과 개행을 완벽하게 처리합니다.

$ touch 'A space-age
new line of vending machines.pdf'
$ find . -iname '*space*' -print0 | xargs -0 -n 1 ./echoArgs.sh

다음과 같은 일반적인 솔루션에서 특히 문제가 될 수 있습니다.

chmod +x ./echoArgs.sh
for file in $(ls *spacey*); do
  ./echoArgs.sh "$file"
done

노트


답변

* nix 툴 세트와 함께 파일 (이름에 공백이 포함 된 파일 포함)을 처리하는 데 상당히 적합 bash하기 때문에 bashers에 동의하지 않습니다 bash.

실제로, find어떤 파일을 처리할지 선택하는 것에 대해 세밀한 제어가 가능합니다. bash 측에서는 문자열을 다음과 같이 만들어야한다는 사실 만 알아야합니다 bash words. 일반적으로 “큰 따옴표”또는 IFS 사용과 같은 다른 메커니즘을 사용하거나{}

대부분의 / 많은 상황에서 IFS를 설정하고 재설정 할 필요는 없습니다. 아래 예와 같이 IFS를 로컬로 사용하십시오. 세 가지 모두 공백을 처리합니다. 또한 find \; 사실상 루프 이기 때문에 “표준”루프 구조가 필요하지 않습니다 . 루프 로직을 bash 함수에 넣으십시오 (표준 도구를 호출하지 않는 경우).

IFS=$'\n' find ~/ -name '*.txt' -exec  function-or-util {} \;

그리고 두 가지 예

IFS=$'\n' find ~/ -name '*.txt' -exec  printf 'Hello %s\n' {} \;
IFS=$'\n' find ~/ -name '*.txt' -exec  echo {} \+ |sed 's/home//'

‘찾기 also allows you to pass multiple filenames as args to you script ..(if it suits your need: use+ instead\;`)