변수에서 공백을 처리하는 방법

변수에서 공백을 처리하는 방법

저는 몇 가지 스크립트를 작성하고 있습니다.

for x in `find ./ -name *.pdf`
do
  echo pathname $x
done

내 파일 이름은 입니다 Test1 ( Volume II).Test2 ( Volume II)

pathname Test1
pathname (
pathname Volume
pathname II

어떻게 변수로 유지합니까?

답변1

이 사이트에서 여러 번 언급했듯이 Bourne/POSIX 쉘에서 따옴표가 없는 변수 확장(예: 또는 $var) 또는 명령 대체(예: 또는 대부분의 쉘에서 산술 확장(예: ) )는 분할+glob 연산 기호입니다.`cmd`$(cmd)$((11 * 11))

내용 $var또는 출력 (후행 개행 없음) 은 특수 변수(기본적으로 SPC, TAB 및 NL 문자(및 의 NUL ) 포함)의 현재 값과 해당 변수에 의해 생성된 각 단어를 cmd기준으로 분할됩니다 . 파일 이름에 의해 생성됩니다.$IFSzsh와일드카드.

예를 들어, find출력 ./foo bar.pdf\n./*foo*\tbar.pdf\n( \tTAB 및 \nNL을 나타냄)이 기본값 인 경우 명령 대체는 (후행 줄 바꿈 제거)로 확장된 다음 , , 및 로 분할 $IFS됩니다. 여기서 와일드카드 패턴은 인수 수만큼 확장됩니다. 현재 디렉토리에 있는 것처럼 Exists 이름에는 ../foo bar.pdf\n./*foo*\tbar.pdf./foobar.pdf./*foo*foo.pdf./*foo*foo

개행 문자로만 분할하려면 $IFS다음과 같이 개행 문자로만 설정해야 합니다.

IFS='
'

와일드카드 패턴을 확장하지 않으려면 다음을 사용하여 비활성화해야 합니다.

set -f

그러나 줄 바꿈은 파일 이름의 모든 문자만큼 유효하므로 일반적으로 find -print출력은 안정적으로 사후 처리될 수 없습니다.

출력은 다음과 같습니다.

./a.pdf
./b.pdf

현재 디렉토리의 및 파일을 나타내 거나 디렉토리에서 호출된 파일을 a.pdf나타냅니다 .b.pdfb.pdfa.pdf\n.

GNU (GNU에서 유래) find와 같은 일부 구현에는 NL 문자 대신 NUL 문자가 뒤에 오는 파일 이름을 출력하는 조건자가 있습니다. 표준을 사용하면 동일한 결과를 얻을 수 있습니다 . NUL은 파일 이름에 나타날 수 없는 유일한 문자입니다.find-print0find-exec printf '%s\0' {} +

그러나 zsh변수에 NUL 문자(예: 문자)를 저장할 수 있는 유일한 셸이므로 $IFS다음과 같습니다.

IFS=$'\0'
for i in `find ... -print0`; do
  ...
done

( 명령 대체 시 와일드카드가 수행되지 않으므로 in은 필요하지 않습니다.) 다른 쉘에서는 작동 하지만 set -f다른 쉘에서는 작동하지 않습니다.zshzshzsh

가장 이식 가능한 방법은 find이러한 파일에서 실행하려는 명령을 호출하는 것입니다. @Gnouc가 제안한 대로:

find . -name '*.pdf' -exec the command {} \;

쉘 문과 관련된 좀 더 복잡한 작업이 필요한 경우에도 다음을 수행할 수 있습니다.

find . -name '*.pdf' -exec sh -c '
  for i do
    something complex with "$i"
  done' sh {} +

zsh또는 를 사용하여 bash다음을 수행할 수도 있습니다.

find . -name '*.pdf' -print0 |
  while IFS= read -r -d '' file; do
    whatever with "$file"
  done

그러나 루프 내의 표준 입력은 영향을 받습니다.

zshfind(1990년 이후)에는 구문을 통해 와일드카드 기능에 대부분의 기능이 포함되어 있습니다. 여기서는 모든 수준의 하위 디렉터리( (*/)#문법 또는 더 간단한 형식)와 와일드카드 한정자( **/위젯에서 , . ..)를 지정할 수 있습니다.-type f-mtime-permfind

그 중 일부는 2003년, 2005년, 2009년, 2010년에 재생산되었습니다 **/(이 섹션도 재생산되었지만). 그리고 그 중 어느 것도 기본적으로 활성화하지 않습니다. 불행하게도 및 는 디렉토리(예: /in 또는 in or)에 대한 심볼릭 링크를 따릅니다.ksh93fishbashtcshtcsh***/bashfish **-L-followfind***zshtcsh

이러한 셸에서는 파일에 의존하지 않고 모든 수준의 하위 디렉터리에서 파일을 찾을 수 있지만 위의 경고와 globbing 한정자에만 pdf주의하세요 .findfishbashzsh

예를 들어 이는 다음 zsh과 같습니다.

find . -name '*.pdf' -type f -exec ls -ld {} +

할 것이다:

ls -ld ./**/*.pdf(D.)

이를 사용할 때 bash다음을 수행해야 합니다.

shopt -s failglob
shopt -s globstar
files=(./**/*.pdf) &&
  for i do
    [ -f "$i" ] && ! [ -L "$i" ] && set -- "$i" "$@"
    shift
  done && ls -ld "$@"

답변2

가장 안전한 방법은 globbing을 사용하는 것입니다.

for file in *pdf; do echo pathname "$file"; done

모든 PDF를 재귀적으로 찾으려면 다음을 수행하십시오.

shopt -s globstar
for file in **/*pdf; do echo pathname "$file"; done

답변3

다음 줄을 시도해 보세요:

find ./ -name "*.pdf" -exec echo pathname {} \;

답변4

기본 FreeBSD 셸(/bin/sh)에 대한 솔루션을 찾는 데 몇 시간을 보낸 후 마침내 나만의 솔루션을 생각해냈습니다. 공백을 파괴/생성하기 위해 sed를 사용하는 것이 포함됩니다. 또한 while 루프(/bin/sh에 하위 쉘을 생성함)를 사용하는 것과는 달리 루프 내부에서 변수를 변경하고 외부에서 사용할 수도 있습니다.

REMOTE_FOLDER=/media/images
FILE_LIST=$(find $REMOTE_FOLDER | sed 's/ /\/\/\/\//g')
for FILE in $FILE_LIST; do
    FILE=$(echo $FILE | sed 's/\/\/\/\// /g')
    # do whatever you need to do with $FILE
done

관련 정보