요소에 공백이있는 배쉬 배열


150

카메라에서 파일 이름을 bash로 배열을 만들려고합니다.

FILES=(2011-09-04 21.43.02.jpg
2011-09-05 10.23.14.jpg
2011-09-09 12.31.16.jpg
2011-09-11 08.43.12.jpg)

보다시피, 각 파일 이름 중간에 공백이 있습니다.

각 이름을 따옴표로 묶고 백 슬래시로 공백을 이스케이프 처리하려고 시도했지만 둘 다 작동하지 않습니다.

배열 요소에 액세스하려고하면 공간을 요소 구분 기호로 계속 취급합니다.

이름 안에 공백이있는 파일 이름을 올바르게 캡처하려면 어떻게해야합니까?


구식으로 파일을 추가해 보셨습니까? 처럼 FILES[0] = ...? (편집 : 나는 방금했다; 작동하지 않습니다. 재미있는).
Dan Fego


Cygwin을 사용하여 여기에있는 모든 대답을 나눕니다. 파일 이름, 마침표에 공백이 있으면 이상한 일을합니다. 작업하려는 모든 요소의 텍스트 파일 목록에 "배열"을 작성하고 파일의 행을 반복하여이 문제를 해결합니다. 형식화는 여기에 괄호 안의 명령을 둘러싼 의도 된 백틱으로 모입니다. IFS = ""; 배열 = ( find . -maxdepth 1 -type f -iname \*.$1 -printf '%f\n'); $ {array [@]}의 요소 echo $ element; 완료
Alex Hall

답변:


121

요소에 액세스하는 방법에 부분적으로 문제가 있다고 생각합니다. 간단한 작업을 수행 for elem in $FILES하면 동일한 문제가 발생합니다. 그러나 인덱스를 통해 배열에 액세스하면 요소를 숫자로 또는 이스케이프로 추가하면 작동합니다.

for ((i = 0; i < ${#FILES[@]}; i++))
do
    echo "${FILES[$i]}"
done

이러한 선언 $FILES은 모두 작동해야합니다.

FILES=(2011-09-04\ 21.43.02.jpg
2011-09-05\ 10.23.14.jpg
2011-09-09\ 12.31.16.jpg
2011-09-11\ 08.43.12.jpg)

또는

FILES=("2011-09-04 21.43.02.jpg"
"2011-09-05 10.23.14.jpg"
"2011-09-09 12.31.16.jpg"
"2011-09-11 08.43.12.jpg")

또는

FILES[0]="2011-09-04 21.43.02.jpg"
FILES[1]="2011-09-05 10.23.14.jpg"
FILES[2]="2011-09-09 12.31.16.jpg"
FILES[3]="2011-09-11 08.43.12.jpg"

6
배열 요소 (예 :)를 사용할 때는 큰 따옴표를 사용해야합니다 echo "${FILES[$i]}". 중요하지 않지만 echo파일 이름으로 사용하는 모든 항목에 적용됩니다.
Gordon Davisson

26
로 요소를 반복 할 수있을 때 색인을 반복 할 필요는 없습니다 for f in "${FILES[@]}".
Mark Edgar

10
@MarkEdgar 배열 구성원에 공백이있을 때 $ {FILES [@]}에서 f에 대해 문제가 발생했습니다 . 전체 배열은 기존 멤버를 두 개 이상의 요소로 나누는 공백으로 다시 해석되는 것 같습니다. ""는 매우 중요한 것 같습니다
Michael Shaw

1
날카로운 ( #) 기호는 for ((i = 0; i < ${#FILES[@]}; i++))진술에서 무엇을합니까?
Michal Vician

4
나는 6 년 전이 대답하지만 나는 그것이 얻을 믿을 배열 파일의 요소의 수를.
Dan Fego

91

배열의 항목에 액세스하는 방식에 문제가 있어야합니다. 방법은 다음과 같습니다.

for elem in "${files[@]}"
...

로부터 배쉬 맨 페이지 :

배열의 모든 요소는 $ {name [subscript]}를 사용하여 참조 할 수 있습니다. ... 첨자가 ​​@ 또는 * 인 경우 단어가 모든 이름 구성원으로 확장됩니다. 이 첨자는 단어가 큰 따옴표로 묶인 경우에만 다릅니다. 단어가 큰 따옴표로 묶인 경우 $ {name [*]}은 각 배열 구성원의 값을 IFS 특수 변수의 첫 문자로 구분하여 단일 단어로 확장하고 $ {name [@]}은 별도의 단어로 이름을 지정합니다 .

물론 단일 멤버에 액세스 할 때는 큰 따옴표를 사용해야합니다.

cp "${files[0]}" /tmp

3
이 묶음에서 가장 깨끗하고 가장 우아한 솔루션이지만 배열에 정의 된 각 요소를 인용해야한다고 반복해야합니다.
maverick

Dan Fego의 대답은 효과적이지만 요소의 공백을 처리하는 더 관용적 인 방법입니다.
Daniel Zhang

3
다른 프로그래밍 언어에서 유래 한이 발췌 부분의 용어는 실제로 이해하기 어렵습니다. 또한 구문은 당황 스럽습니다. 조금 더 들어가면 정말 고맙겠어요? 특히expands to a single word with the value of each array member separated by the first character of the IFS special variable
CL22

1
그렇습니다. 큰 따옴표로 해결하고 다른 솔루션보다 낫다는 데 동의하십시오. 추가 설명-대부분의 다른 사람들은 큰 따옴표가 부족합니다. 당신은 올바른 : for elem in "${files[@]}"을 가지고 있지만 for elem in ${files[@]}-공백은 확장을 혼란스럽게하고 개별 단어에서 실행하려고 시도합니다.
arntg

"GNU bash, 버전 3.2.57 (1)-릴리스 (x86_64-apple-darwin18)"를 사용하는 macOS 10.14.4에서는 작동하지 않습니다. 이전 버전의 bash에 버그가 있습니까?
Mark Ribau

43

공간을 요소 분리 문자로 중지하려면 IFS를 사용해야합니다.

FILES=("2011-09-04 21.43.02.jpg"
       "2011-09-05 10.23.14.jpg"
       "2011-09-09 12.31.16.jpg"
       "2011-09-11 08.43.12.jpg")
IFS=""
for jpg in ${FILES[*]}
do
    echo "${jpg}"
done

를 기준으로 분리하려는 경우. 그런 다음 IFS = "를 수행하십시오." 그것이 당신을 도울 수 있기를 바랍니다 :)


3
배열 할당 이전으로 IFS = ""를 옮겨야했지만 이것이 정답입니다.
rob

정보를 파싱하기 위해 여러 배열을 사용하고 있으며 IFS = ""가 그중 하나에서만 작동하는 효과가 있습니다. IFS = ""를 사용하면 다른 모든 배열의 구문 분석이 중지됩니다. 이것에 대한 힌트가 있습니까?
Paulo Pedroso

Paulo, 귀하의 경우에 더 좋은 또 다른 답변을보십시오 : stackoverflow.com/a/9089186/1041319 . IFS = ""를 시도하지 않았지만 우아하게 해결 된 것 같습니다. 그러나 일부 사례에서는 왜 문제가 발생할 수 있는지 보여줍니다. IFS = ""를 한 줄에 설정하는 것이 가능할 수 있지만 여전히 다른 솔루션보다 더 혼란 스러울 수 있습니다.
arntg

그것은 또한 bash에서 나를 위해 일했다. 감사합니다 @Khushneet 30 분 동안 검색했습니다…
csonuryilmaz

잘 작동 한이 페이지에 대해서만 답변하십시오. 그러나 나는 또한 IFS="" 배열 구성 전에 를 옮겨야했다 .
pkamb

13

문제의 요소에 어떻게 접근하고 있는지 다른 사람들과 동의합니다. 배열 할당에서 파일 이름을 인용하면 올바른 것입니다.

FILES=(
  "2011-09-04 21.43.02.jpg"
  "2011-09-05 10.23.14.jpg"
  "2011-09-09 12.31.16.jpg"
  "2011-09-11 08.43.12.jpg"
)

for f in "${FILES[@]}"
do
  echo "$f"
done

양식의 배열 주위에 큰 따옴표를 사용 "${FILES[@]}"하면 배열을 배열 요소 당 하나의 단어로 분할합니다. 그 이상으로 단어를 나누지 않습니다.

사용 "${FILES[*]}"은 특별한 의미를 갖지만 배열 요소를 $ IFS의 첫 문자와 결합 하여 단어를 생성하므로 원하는 단어가 아닐 수 있습니다.

베어 ${array[@]}또는 ${array[*]}주제를 사용하면 확장 결과가 단어 분리로 이어 지므로 $IFS배열 요소 당 하나의 단어 대신 공백 (및 기타 단어)으로 분할 된 단어로 끝납니다 .

C 스타일 for 루프를 사용하는 것도 좋으며 명확하지 않은 경우 단어 분리에 대해 걱정하지 않아도됩니다.

for (( i = 0; i < ${#FILES[@]}; i++ ))
do
  echo "${FILES[$i]}"
done

3

탈출이 작동합니다.

#!/bin/bash

FILES=(2011-09-04\ 21.43.02.jpg
2011-09-05\ 10.23.14.jpg
2011-09-09\ 12.31.16.jpg
2011-09-11\ 08.43.12.jpg)

echo ${FILES[0]}
echo ${FILES[1]}
echo ${FILES[2]}
echo ${FILES[3]}

산출:

$ ./test.sh
2011-09-04 21.43.02.jpg
2011-09-05 10.23.14.jpg
2011-09-09 12.31.16.jpg
2011-09-11 08.43.12.jpg

문자열을 인용해도 동일한 출력이 생성됩니다.


3

다음과 같이 배열이 있다면 #! / bin / bash

Unix[0]='Debian'
Unix[1]="Red Hat"
Unix[2]='Ubuntu'
Unix[3]='Suse'

for i in $(echo ${Unix[@]});
    do echo $i;
done

당신은 얻을 것이다 :

Debian
Red
Hat
Ubuntu
Suse

왜 그런지 모르겠지만 루프는 공백을 세분화하여 개별 항목으로 넣습니다. 심지어 따옴표로 묶습니다.

이 문제를 해결하려면 배열의 요소를 호출하는 대신 인덱스를 호출하면 따옴표로 묶인 전체 문자열이 사용됩니다. 따옴표로 묶어야합니다!

#!/bin/bash

Unix[0]='Debian'
Unix[1]='Red Hat'
Unix[2]='Ubuntu'
Unix[3]='Suse'

for i in $(echo ${!Unix[@]});
    do echo ${Unix[$i]};
done

그럼 당신은 얻을 것이다 :

Debian
Red Hat
Ubuntu
Suse

2

원래 질문의 인용 / 이스케이프 문제에 대한 정확한 대답은 아니지만 실제로 op에 더 유용했을 것입니다.

unset FILES
for f in 2011-*.jpg; do FILES+=("$f"); done
echo "${FILES[@]}"

물론, 표현은 특정 요구 사항 (예 *.jpg를 들어 2001-09-11*.jpg, 특정 날짜의 그림 전체 또는 전체) 에 따라 채택되어야한다 .


0

또 다른 해결책은 "while"루프 대신 "for"루프를 사용하는 것입니다.

index=0
while [ ${index} -lt ${#Array[@]} ]
  do
     echo ${Array[${index}]}
     index=$(( $index + 1 ))
  done

0

를 사용하지 않으면 bash파일 이름에서 공백을 다르게 처리하는 것이 fish shell 의 장점 중 하나입니다 . "a b.txt"및 "b c.txt"라는 두 개의 파일이있는 디렉토리를 고려하십시오. 로 다른 명령에서 생성 된 파일 목록을 처리 할 때 합리적인 추측을 bash할 수 있지만 경험 한 파일 이름의 공백으로 인해 실패합니다.

# bash
$ for f in $(ls *.txt); { echo $f; }
a
b.txt
b
c.txt

를 사용 fish하면 구문이 거의 동일하지만 결과는 다음과 같습니다.

# fish
for f in (ls *.txt); echo $f; end
a b.txt
b c.txt

물고기는 공백이 아니라 줄 바꿈으로 명령의 출력을 나누기 때문에 다르게 작동합니다.

줄 바꿈 대신 공백으로 분할하려는 경우 fish매우 읽기 쉬운 구문이 있습니다.

for f in (ls *.txt | string split " "); echo $f; end

0

IFS 값 을 재설정하고 완료되면 롤백했습니다.

# backup IFS value
O_IFS=$IFS

# reset IFS value
IFS=""

FILES=(
"2011-09-04 21.43.02.jpg"
"2011-09-05 10.23.14.jpg"
"2011-09-09 12.31.16.jpg"
"2011-09-11 08.43.12.jpg"
)

for file in ${FILES[@]}; do
    echo ${file}
done

# rollback IFS value
IFS=${O_IFS}

루프에서 가능한 출력 :

2011-09-04 21.43.02.jpg

2011-09-05 10.23.14.jpg

2011-09-09 12.31.16.jpg

2011-09-11 08.43.12.jpg

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.