bash 쉘에서 하나의 문자열을 하나 이상의 공백으로 구분하여 여러 문자열로 나누는 방법은 무엇입니까?


224

각 두 단어 사이에 하나 이상의 공백이있는 많은 단어가 포함 된 문자열이 있습니다. 문자열을 개별 단어로 분리하여 반복 할 수 있습니까?

문자열은 인수로 전달됩니다. 예 ${2} == "cat cat file". 어떻게 반복 할 수 있습니까?

또한 문자열에 공백이 있는지 어떻게 확인할 수 있습니까?


1
어떤 종류의 껍질? 배쉬, cmd.exe, powershell ...?
Alexey Sviridov

반복해야합니까 (예 : 각 단어마다 명령을 실행)? 아니면 나중에 사용하기 위해 단어 목록을 저장해야합니까?
DVK

답변:


281

문자열 변수를 for루프에 전달하려고 했습니까 ? Bash는 공백으로 자동 분할됩니다.

sentence="This is   a sentence."
for word in $sentence
do
    echo $word
done

 

This
is
a
sentence.

1
@MobRule-이것의 유일한 단점은 추가 처리를 위해 출력을 쉽게 캡처 할 수 없다는 것입니다 (적어도 방법을 기억하지 못합니다). STDOUT으로 물건을 보내는 내용은 아래의 "tr"솔루션을 참조하십시오
DVK

4
변수에 추가하면됩니다 : A=${A}${word}).
Lucas Jones

1
$ text 설정 [이것은 $ 1, $ 2, $ 3 ... 등에 단어를 넣을 것입니다]
Rajesh

32
실제로이 트릭은 잘못된 솔루션 일뿐만 아니라 쉘 글러브로 인해 매우 위험 합니다. 예상 대신 touch NOPE; var='* a *'; for a in $var; do echo "[$a]"; done출력 (LF는 가독성을 위해 SPC로 대체 됨). [NOPE] [a] [NOPE][*] [a] [*]
티노

@mob 특정 문자열을 기반으로 문자열을 분할하려면 어떻게해야합니까? 예를 들어 ".xlsx" separator.

296

개별 요소에 액세스 할 수 있도록 배열로 변환하는 것이 좋습니다.

sentence="this is a story"
stringarray=($sentence)

이제 개별 요소에 직접 액세스 할 수 있습니다 (0으로 시작).

echo ${stringarray[0]}

또는 반복하기 위해 문자열로 다시 변환하십시오.

for i in "${stringarray[@]}"
do
  :
  # do whatever on $i
done

물론 문자열을 통한 루핑은 이전에 답변되었지만 그 대답은 나중에 사용하기 위해 개별 요소를 추적하지 않는 단점이있었습니다.

for i in $sentence
do
  :
  # do whatever on $i
done

Bash Array Reference참조하십시오 .


26
쉘 글 로빙으로 인해 슬프게도 완벽하지는 않습니다 : 예상 대신 touch NOPE; var='* a *'; arr=($var); set | grep ^arr=출력arr=([0]="NOPE" [1]="a" [2]="NOPE")arr=([0]="*" [1]="a" [2]="*")
Tino

@ 티노 : 글 로빙이 방해하는 것을 원하지 않으면 단순히 끄십시오. 그런 다음이 솔루션은 와일드 카드에서도 잘 작동합니다. 내 의견으로는 가장 좋은 방법입니다.
Alexandros

3
@Alexandros 내 접근 방식은 기본적으로 안전하고 모든 컨텍스트에서 완벽하게 작동하는 패턴 만 사용하는 것입니다. 안전한 솔루션을 얻기 위해 쉘 글 로빙을 변경해야하는 요구는 매우 위험한 경로 이상의 것이 아니라 이미 어두운면입니다. 그래서 내 조언은 조만간 세부 사항을 잊어 버리고 누군가가 버그를 악용하기 때문에 이와 같은 패턴을 사용하는 데 익숙해지지 않는 것입니다. 언론에서 그러한 악용에 대한 증거를 찾을 수 있습니다. 마다. 단일. 일.
티노

86

쉘 "set"내장을 사용하십시오. 예를 들어

$ text 설정

그 후 $ text의 개별 단어는 $ 1, $ 2, $ 3 등이됩니다. 견고성을 위해 보통

세트-정크 $ 텍스트
시프트

$ text가 비어 있거나 대시로 시작하는 경우를 처리합니다. 예를 들면 다음과 같습니다.

text = "테스트입니다"
세트-정크 $ 텍스트
시프트
말을 위해; 하다
  에코 "[$ word]"
끝난

이것은 인쇄

[이]
[is]
[ㅏ]
[테스트]

5
이는 개별 부분에 직접 액세스 할 수 있도록 var를 분할하는 훌륭한 방법입니다. +1; 내 문제를 해결
Cheekysoft

나는 사용을 제안하려고 awk했지만 set훨씬 쉽습니다. 나는 지금 set팬보이입니다. 감사합니다 @Idelic!
이즈미르 라미레즈

22
다음과 같은 작업을 수행하는 경우 쉘 globbing에 유의하십시오 . 예상 대신 touch NOPE; var='* a *'; set -- $var; for a; do echo "[$a]"; done출력 . 분할 된 문자열에 SHELL 메타 문자가 없음을 101 % 확신하는 경우에만 사용하십시오! [NOPE] [a] [NOPE][*] [a] [*]
티노

4
@Tino : 그 문제뿐만 아니라 여기에, 모든 곳에서 적용되지만이 경우에는 수 만 set -f이전 set -- $varset +f이후에 해제 대체 (globbing)에.
Idelic

3
@Idelic : 잘 잡았습니다. 함께 set -f솔루션도 안전합니다. 그러나 set +f각 쉘의 기본값이므로 필수 세부 사항입니다. 다른 사람들도 아마 그것을 알지 못하기 때문에 주목해야합니다.
티노

81

BASH 3 이상에서 가장 쉽고 안전한 방법은 다음과 같습니다.

var="string    to  split"
read -ra arr <<<"$var"

( arr문자열의 분할 된 부분을 취하는 배열입니다) 또는 입력에 줄 바꿈이 있고 첫 번째 줄 이상을 원할 경우 :

var="string    to  split"
read -ra arr -d '' <<<"$var"

(의 공간은 -d ''남겨 둘 수 없습니다.) 그러나 이것은 예기치 않은 줄 바꿈을 줄 수 있습니다 <<<"$var"(암시 적으로 끝에 LF를 추가하므로).

예:

touch NOPE
var="* a  *"
read -ra arr <<<"$var"
for a in "${arr[@]}"; do echo "[$a]"; done

예상 출력

[*]
[a]
[*]

이 솔루션은 (이전의 모든 솔루션과 달리) 예상치 못하고 종종 제어 할 수없는 쉘 글 로빙이되지 않습니다.

또한 이것은 원하는대로 IFS의 모든 기능을 제공합니다.

예:

IFS=: read -ra arr < <(grep "^$USER:" /etc/passwd)
for a in "${arr[@]}"; do echo "[$a]"; done

다음과 같은 결과가 출력됩니다.

[tino]
[x]
[1000]
[1000]
[Valentin Hilbig]
[/home/tino]
[/bin/bash]

보시다시피, 공백도 이런 식으로 보존 될 수 있습니다 :

IFS=: read -ra arr <<<' split  :   this    '
for a in "${arr[@]}"; do echo "[$a]"; done

출력

[ split  ]
[   this    ]

IFSBASH에서 의 처리는 자체 주제이므로 테스트와 관련하여 흥미로운 주제가 있습니다.

  • unset IFS: SPC, TAB, NL의 실행을 무시하고 온라인 시작 및 종료
  • IFS='': 필드 분리가 필요 없으며 모든 것을 읽습니다.
  • IFS=' ': SPC 실행 (및 SPC 만 해당)

마지막 예

var=$'\n\nthis is\n\n\na test\n\n'
IFS=$'\n' read -ra arr -d '' <<<"$var"
i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done

출력

1 [this is]
2 [a test]

동안

unset IFS
var=$'\n\nthis is\n\n\na test\n\n'
read -ra arr -d '' <<<"$var"
i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done

출력

1 [this]
2 [is]
3 [a]
4 [test]

BTW :

  • 익숙하지 않으면 $'ANSI-ESCAPED-STRING'시간을 절약 할 수 있습니다.

  • 당신이 포함하지 않는 경우 -r(처럼 read -a arr <<<"$var")를 읽어 백 슬래시 이스케이프 않습니다. 이것은 독자의 연습으로 남아 있습니다.


두 번째 질문 :

문자열에서 무언가를 테스트하기 위해 case한 번에 여러 사례를 확인할 수 있으므로 일반적으로 고수합니다 (참고 : fallfall use multiplce case문이 필요한 경우 case는 첫 번째 일치 만 실행 ).이 경우는 종종 (pun 예정된):

case "$var" in
'')                empty_var;;                # variable is empty
*' '*)             have_space "$var";;        # have SPC
*[[:space:]]*)     have_whitespace "$var";;   # have whitespaces like TAB
*[^-+.,A-Za-z0-9]*) have_nonalnum "$var";;    # non-alphanum-chars found
*[-+.,]*)          have_punctuation "$var";;  # some punctuation chars found
*)                 default_case "$var";;      # if all above does not match
esac

따라서 다음과 같이 SPC를 확인하도록 반환 값을 설정할 수 있습니다.

case "$var" in (*' '*) true;; (*) false;; esac

case? 일반적으로 정규식 시퀀스보다 약간 읽기 쉽고 셸 메타 문자 덕분에 모든 요구의 99 %를 잘 처리합니다.


2
이 답변은 강조된 주제와 강조 표시로 인해 더 많은지지를받을 가치가 있습니다.
Brian Agnew

@ 브라이언 감사합니다. 이 컨텍스트에서 쉘 메타 문자가 더 이상 해를 끼치 지 않도록 글 로빙을 사용 set -f하거나 set -o noglob전환 할 수 있습니다 . 그러나 나는 쉘의 많은 힘을 남기고 /이 설정을 전환하는 것이 매우 오류가 많기 때문에 실제로 그 친구는 아닙니다.
Tino

2
훌륭한 답변은 실제로 더 많은 투표를 받아야합니다. 사례가 넘어 질 때주의 할 점- ;&이를 달성 할 수 있습니다 . 어떤 버전의 bash가 있는지 확실하지 않습니다. 저는 4.3 사용자입니다
Sergiy Kolodyazhnyy

2
나는 이것을 아직 알지 못했기 때문에 @ Serg에게 감사드립니다! 그래서 그것을 찾았습니다 .Bash4에 나타났습니다 . ;&C에서와 같이 패턴 검사가없는 강제 감소입니다. 또한 ;;&추가 패턴 검사를 계속 수행하는 것도 있습니다. 그래서 ;;처럼 if ..; then ..; else if ..;;&같다 if ..; then ..; fi; if ..경우, ;&같은있다 m=false; if ..; then ..; m=:; fi; if $m || ..; then ..- 하나 (다른 사람) 학습 결코 멈추지 않는다)
티노

@Tino 그것은 사실입니다-학습은 지속적인 과정입니다. 사실, 난 알고하지 않았다 ;;&D 감사합니다, 그리고 쉘은 당신과 함께있을 수 있습니다) : 당신이 주석 전에
세르지 Kolodyazhnyy에게

43
$ echo "This is   a sentence." | tr -s " " "\012"
This
is
a
sentence.

공백을 확인하려면 grep을 사용하십시오.

$ echo "This is   a sentence." | grep " " > /dev/null
$ echo $?
0
$ echo "Thisisasentence." | grep " " > /dev/null     
$ echo $?
1

1
BASH에서는 echo "X" |일반적으로 다음 <<<"X"과 같이 로 대체 할 수 있습니다 grep -s " " <<<"This contains SPC". 과 echo X | read var달리 무언가를하면 차이를 발견 할 수 있습니다 read var <<< X. 후자는 변수 var를 현재 쉘로 가져 오지만 첫 번째 변형에서 변수 에 액세스하려면 다음과 같이 그룹화해야합니다.echo X | { read var; handle "$var"; }
Tino

17

(A) 문장을 단어로 분리하려면 (공백으로 구분) 간단히 다음을 사용하여 기본 IFS를 사용할 수 있습니다

array=( $string )


다음 코드를 실행

#!/bin/bash

sentence="this is the \"sentence\"   'you' want to split"
words=( $sentence )

len="${#words[@]}"
echo "words counted: $len"

printf "%s\n" "${words[@]}" ## print array

출력합니다

words counted: 8
this
is
the
"sentence"
'you'
want
to
split

보시다시피 아무 문제없이 작은 따옴표 나 큰 따옴표도 사용할 수 있습니다

. 참고 :
-이것은 기본적으로 mob 의 대답 과 동일 하지만 더 필요한 경우 배열을 저장합니다. 단일 루프 만 필요한 경우 그의 대답을 사용할 수 있습니다. 한 줄이 더 짧습니다 :)
- 구분 기호를 기준으로 문자열을 분할하는 다른 방법에 대해서는 이 질문 을 참조하십시오 .


(B) 문자열에서 문자를 확인하기 위해 정규식 일치를 사용할 수도 있습니다.
사용할 수있는 공백 문자가 있는지 확인하는 예 :

regex='\s{1,}'
if [[ "$sentence" =~ $regex ]]
    then
        echo "Space here!";
fi

정규식 힌트 (B)의 경우 +1이지만 잘못된 솔루션 (A)의 경우 -1입니다. 이는 쉘 글로 빙하기 쉬운 오류입니다. ;)
Tino

6

bash로 공간을 확인하려면 다음을 수행하십시오.

[[ "$str" = "${str% *}" ]] && echo "no spaces" || echo "has spaces"

1
echo $WORDS | xargs -n1 echo

모든 단어를 출력하므로 나중에 적합하다고 생각되는대로 해당 목록을 처리 할 수 ​​있습니다.

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