Nice programing

Bash에서 문자열을 배열로 분할

nicepro 2020. 10. 3. 11:51
반응형

Bash에서 문자열을 배열로 분할


Bash 스크립트에서 줄을 조각으로 나누고 배열에 저장하고 싶습니다.

라인 :

Paris, France, Europe

다음과 같은 배열로 만들고 싶습니다.

array[0] = Paris
array[1] = France
array[2] = Europe

간단한 코드를 사용하고 싶습니다. 명령의 속도는 중요하지 않습니다. 어떻게하니?


IFS=', ' read -r -a array <<< "$string"

문자에서 유의 $IFS이 경우 필드로 분리 될 수 있도록 분리되어 개별적으로 처리 하거나 쉼표 또는 공백이 아닌 두 문자의 시퀀스. 하지만 흥미롭게도 입력에 쉼표 공백이 나타날 때 공백이 특별히 처리되기 때문에 빈 필드가 생성되지 않습니다.

개별 요소에 액세스하려면 :

echo "${array[0]}"

요소를 반복하려면 :

for element in "${array[@]}"
do
    echo "$element"
done

인덱스와 값을 모두 얻으려면 :

for index in "${!array[@]}"
do
    echo "$index ${array[index]}"
done

마지막 예제는 Bash 배열이 희소하기 때문에 유용합니다. 즉, 요소를 삭제하거나 요소를 추가하면 인덱스가 연속적이지 않습니다.

unset "array[1]"
array[42]=Earth

배열의 요소 수를 얻으려면 :

echo "${#array[@]}"

위에서 언급했듯이 배열은 희소 할 수 있으므로 길이를 사용하여 마지막 요소를 가져 오지 않아야합니다. Bash 4.2 이상에서 할 수있는 방법은 다음과 같습니다.

echo "${array[-1]}"

모든 버전의 Bash (2.05b 이후) :

echo "${array[@]: -1:1}"

더 큰 음수 오프셋은 어레이 끝에서 더 멀리 선택됩니다. 이전 양식에서 빼기 기호 앞의 공백에 유의하십시오. 필수입니다.


이 질문에 대한 모든 대답은 어떤 식 으로든 잘못되었습니다.


오답 # 1

IFS=', ' read -r -a array <<< "$string"

1 : 이것은 $IFS. 의 값은 $IFS변수입니다 하지 A와 촬영 단일 가변 길이 오히려 그것이로한다 문자열 세퍼레이터 세트단일 문자의 각 필드는 해당 문자열의 분리, read입력 라인으로부터 벗어난 분열이 종료 될 수 있는 세트에서 문자 ( 이 예에서는 쉼표 또는 공백).

실제로, 진짜 고집쟁이들에게는의 전체 의미 $IFS가 약간 더 관련되어 있습니다. 로부터 bash는 설명서 :

쉘은 IFS의 각 문자를 구분 기호로 취급 하고 이러한 문자를 필드 종결 자로 사용하여 다른 확장의 결과를 단어로 분할합니다. 경우 IFS가 설정되지 않은 경우, 또는 그 값이 정확히 <스페이스> <탭> <개행 문자> , 기본의 다음 순서 <공간> , <탭><줄 바꿈> 시작과 이전 확장의 결과의 끝 무시 되며 시작 또는 끝에없는 IFS 문자 시퀀스는 단어를 구분하는 역할을합니다. IFS 에 기본값이 아닌 값이있는 경우 공백 문자 <space> , <tab><공백 문자가 IFS ( IFS 공백 문자) 값에있는 한 단어의 시작과 끝에서 무시됩니다 . 의 모든 문자 IFS 아닌 IFS 인접한과 함께 공백을 IFS , 필드을 구분 공백 문자. 일련의 IFS 공백 문자도 구분 기호로 처리됩니다. IFS이 널이면 단어 분할이 발생하지 않습니다.

기본적으로의 기본값이 아닌 null이 아닌 값의 $IFS경우 필드를 (1) "IFS 공백 문자"집합에있는 하나 이상의 문자 시퀀스 (즉, <space> 중 하나)로 구분할 수 있습니다 . <tab><newline> ( "newline"은 줄 바꿈 (LF)을 의미 ) ) $IFS또는 (2) "IFS 공백 문자" $IFS와 함께 표시 되는 비 "IFS 공백 문자" 입력 줄에.

OP의 경우 이전 단락에서 설명한 두 번째 분리 모드가 입력 문자열에 대해 정확히 원하는 것일 수 있지만 내가 설명한 첫 번째 분리 모드가 전혀 정확하지 않다고 확신 할 수 있습니다. 예를 들어 입력 문자열이 'Los Angeles, United States, North America'?

IFS=', ' read -ra a <<<'Los Angeles, United States, North America'; declare -p a;
## declare -a a=([0]="Los" [1]="Angeles" [2]="United" [3]="States" [4]="North" [5]="America")

2 : 이 솔루션을 단일 문자 구분 기호 (예 : 쉼표 자체, 즉 뒤 따르는 공백이나 기타 수하물 없음)와 함께 사용하더라도 $string변수 값에 LF가 포함 read되면 첫 번째 LF를 만나면 처리를 중지합니다. read내장에만 호출 당 한 줄을 처리합니다. here-string 메커니즘 을 사용하여이 예제에서 수행하는 것처럼 입력 명령문으로 파이핑하거나 리디렉션하는 경우에도 마찬가지 이므로 처리되지 않은 입력은 손실이 보장됩니다. 내장을 구동 하는 코드 는 포함 된 명령 구조 내의 데이터 흐름에 대해 알지 못합니다.readread

이것이 문제를 일으키지 않을 것이라고 주장 할 수 있지만, 가능하면 피해야하는 미묘한 위험입니다. 이는 read내장이 실제로 두 단계의 입력 분할을 수행 한다는 사실에 기인합니다 . 처음에는 행으로, 다음에는 필드로 분할합니다. OP는 한 수준의 분할만을 원하기 때문에 이러한 read내장 사용은 적절하지 않으며이를 피해야합니다.

3 : 이 솔루션의 분명하지 않은 잠재적 인 문제 read는 비어있는 경우 항상 후행 필드를 삭제하지만 그렇지 않으면 빈 필드를 유지한다는 것입니다. 다음은 데모입니다.

string=', , a, , b, c, , , '; IFS=', ' read -ra a <<<"$string"; declare -p a;
## declare -a a=([0]="" [1]="" [2]="a" [3]="" [4]="b" [5]="c" [6]="" [7]="")

OP는 이것에 대해 신경 쓰지 않을지도 모르지만 여전히 알 가치가있는 한계입니다. 솔루션의 견고성과 일반성을 감소시킵니다.

이 문제는 read나중에 설명 하겠지만 입력 문자열을에 공급하기 직전에 더미 후행 구분 기호를 입력 문자열에 추가하여 해결할 수 있습니다 .


오답 # 2

string="1:2:3:4:5"
set -f                     # avoid globbing (expansion of *).
array=(${string//:/ })

비슷한 아이디어 :

t="one,two,three"
a=($(echo $t | tr ',' "\n"))

(참고 : 응답자가 생략 한 것으로 보이는 명령 대체 주위에 누락 된 괄호를 추가했습니다.)

비슷한 아이디어 :

string="1,2,3,4"
array=(`echo $string | sed 's/,/\n/g'`)

이러한 솔루션은 배열 할당에서 단어 분할을 활용하여 문자열을 필드로 분할합니다. 재미있게도 read, 일반 단어 분할도 $IFS특수 변수를 사용하지만이 경우 기본값 인 <space> <tab> <newline> 으로 설정되어 있으므로 하나 이상의 IFS 시퀀스로 설정되어 있음을 암시합니다. 문자 (지금은 모두 공백 문자 임)는 필드 구분 기호로 간주됩니다.

이것은 read단어 분할 자체가 하나의 분할 수준 만 구성하기 때문에에서 커밋 된 두 수준의 분할 문제를 해결합니다 . 그러나 이전과 마찬가지로 여기서 문제는 입력 문자열의 개별 필드에 이미 $IFS문자 가 포함될 수 있으므로 단어 분할 작업 중에 부적절하게 분할된다는 것입니다. 이것은 이러한 응답자가 제공하는 샘플 입력 문자열의 경우가 아니지만 (얼마나 편리한 지 ...) 물론이 관용구를 사용하는 코드베이스가 다음 위험을 실행한다는 사실을 변경하지는 않습니다. 이 가정이 선 아래의 어느 시점에서 위반 된 적이 있다면 폭파됩니다. 다시 한 번 'Los Angeles, United States, North America'(또는 'Los Angeles:United States:North America')의 반례를 고려하십시오 .

또한, 단어 분할은 일반적으로 뒤 따른다 파일명 확장 ( 일명 패스 팽창 일명 , 완료되면, 문자를 포함하는 잠재적 손상 단어 것이다 로빙) *, ?또는 [다음에 ](그리고 있다면, extglob설정, 괄호 단편 앞에는 ?, *, +, @, 또는 !) 파일 시스템 개체와 일치시키고 그에 따라 단어 ( "globs")를 확장합니다. 이 세 응답자 중 첫 번째는 글 set -f로빙을 비활성화하기 위해 미리 실행하여이 문제를 교묘하게 완화했습니다. 기술적으로 이것은 작동합니다 (아마도set +f 나중에 종속 될 수있는 후속 코드에 대해 globbing을 다시 활성화 할 수 있지만 로컬 코드에서 기본 문자열-배열 구문 분석 작업을 해킹하기 위해 전역 쉘 설정을 엉망으로 만드는 것은 바람직하지 않습니다.

이 답변의 또 다른 문제는 모든 빈 필드가 손실된다는 것입니다. 이것은 응용 프로그램에 따라 문제가 될 수도 있고 아닐 수도 있습니다.

참고 :이 솔루션을 사용하려는 경우 명령 대체 (셸을 분기 함)를 호출하고 파이프 라인을 시작하는 문제가 발생하는 대신 매개 변수 확장${string//:/ }"패턴 대체"형식 을 사용하는 것이 좋습니다. 매개 변수 확장은 순전히 쉘 내부 작업이므로 외부 실행 파일 ( 또는 ) 실행 (또한 솔루션의 경우 입력 변수는 명령 대체 내부에서 큰 따옴표로 묶어야합니다. 그렇지 않으면 단어 분할이 명령에 영향을 미치고 잠재적으로 필드 값을 엉망으로 만들 수 있습니다. 또한 명령 대체 형식이 이전 명령보다 선호됩니다.trsedtrsedecho$(...)`...` 이는 명령 대체의 중첩을 단순화하고 텍스트 편집기에서 더 나은 구문 강조를 허용하기 때문입니다.)


오답 # 3

str="a, b, c, d"  # assuming there is a space after ',' as in Q
arr=(${str//,/})  # delete all occurrences of ','

이 대답은 # 2 와 거의 같습니다 . 차이점은 응답자가 필드가 두 문자로 구분된다는 가정을했다는 것입니다.이 중 하나는 기본값으로 표시되고 다른 하나는 표시 $IFS되지 않습니다. 그는 패턴 대체 확장을 사용하여 비 IFS 표현 문자를 제거한 다음 단어 분할을 사용하여 살아남은 IFS 표현 구분 문자에서 필드를 분할함으로써 다소 특정한 경우를 해결했습니다.

이것은 매우 일반적인 솔루션이 아닙니다. 게다가 여기서 쉼표는 실제로 "주"구분 문자이며, 쉼표를 제거하고 필드 분할을 위해 공백 문자에 의존하는 것은 단순히 잘못된 것이라고 주장 할 수 있습니다. 다시 한 번 내 반례를 고려하십시오 'Los Angeles, United States, North America'.

또한 파일 이름 확장으로 인해 확장 된 단어가 손상 될 수 있지만 할당에 대한 globbing을 일시적으로 비활성화 set -f한 다음 을 사용하여이를 방지 할 수 있습니다 set +f.

또한 모든 빈 필드가 손실되며 응용 프로그램에 따라 문제가 될 수도 있고 아닐 수도 있습니다.


오답 # 4

string='first line
second line
third line'

oldIFS="$IFS"
IFS='
'
IFS=${IFS:0:1} # this is useful to format your code with tabs
lines=( $string )
IFS="$oldIFS"

이것은 단어 분할을 사용하여 작업을 수행한다는 점에서 # 2# 3유사합니다 . 이제 코드 $IFS는 입력 문자열에있는 단일 문자 필드 구분 기호 만 포함하도록 명시 적으로 설정 합니다. 이것은 OP의 쉼표 구분 기호와 같은 다중 문자 필드 구분 기호에 대해 작동하지 않는다는 것을 반복해야합니다. 그러나이 예에서 사용 된 LF와 같은 단일 문자 구분 기호의 경우 실제로 완벽에 가깝습니다. 필드는 이전의 오답에서 본 것처럼 실수로 중간에서 분할 될 수 없으며 필요에 따라 분할 수준이 하나뿐입니다.

한 가지 문제는 앞서 설명한대로 파일 이름 확장이 영향을받는 단어를 손상시킬 수 있다는 것입니다. 그러나이 문제는 중요한 문을 set -f로 래핑하여 해결할 수 있습니다 set +f.

또 다른 잠재적 인 문제는 LF가 앞에서 정의한 "IFS 공백 문자"로 한정되기 때문에 # 2# 3에서같이 모든 빈 필드가 손실된다는 것 입니다. 물론 구분자가 "IFS 공백 문자"가 아닌 경우 문제가되지 않으며 응용 프로그램에 따라 문제가되지 않을 수도 있지만 솔루션의 일반성을 손상시킵니다.

요약하자면, 한 문자 구분자가 있고 "IFS 공백 문자"가 아닌 문자이거나 빈 필드에 대해 신경 쓰지 않고 중요한 문을 set -fand로 감싸면 set +f이 솔루션이 작동합니다. , 그렇지 않으면 아닙니다.

(또한 정보를 위해 bash의 변수에 LF를 할당하는 것은 $'...'구문을 사용하여 더 쉽게 수행 할 수 있습니다 ( 예 :) IFS=$'\n';.)


오답 # 5

countries='Paris, France, Europe'
OIFS="$IFS"
IFS=', ' array=($countries)
IFS="$OIFS"

비슷한 아이디어 :

IFS=', ' eval 'array=($string)'

이 솔루션은 효과적으로 # 1 ( $IFS쉼표 공백으로 설정)과 # 2-4 (단어 분할을 사용하여 문자열을 필드로 분할 ) 사이의 교차 입니다. 이 때문에 위의 모든 오답을 괴롭히는 대부분의 문제로 고통받습니다.

또한 두 번째 변형의 경우 eval인수가 작은 따옴표로 묶인 문자열 리터럴이므로 정적으로 알려져 있기 때문에 호출이 완전히 불필요한 것처럼 보일 수 있습니다. 그러나 실제로 eval이런 방식 으로 사용하면 매우 분명하지 않은 이점 이 있습니다. 일반적으로 변수 할당으로 구성된 간단한 명령을 실행하면 그 뒤에 실제 명령 단어가없는 것을 의미합니다. 할당은 쉘 환경에서 적용됩니다.

IFS=', '; ## changes $IFS in the shell environment

단순 명령에 여러 변수 할당이 포함 된 경우에도 마찬가지입니다 . 다시 말하지만, 명령 단어가없는 한 모든 변수 할당은 셸 환경에 영향을줍니다.

IFS=', ' array=($countries); ## changes both $IFS and $array in the shell environment

변수 할당이 명령 이름에 연결되어있는 경우, 그때는 않습니다 (나는 이것을 "접두어 할당"전화처럼) 아니 그것은 내장 여부 쉘 환경에 영향을 미치는, 대신에 관계없이, 실행 된 명령의 환경에 영향을 미치는 또는 외부 :

IFS=', ' :; ## : is a builtin command, the $IFS assignment does not outlive it
IFS=', ' env; ## env is an external command, the $IFS assignment does not outlive it

bash 매뉴얼의 관련 인용문 :

명령 이름 결과가 없으면 변수 할당이 현재 셸 환경에 영향을줍니다. 그렇지 않으면 변수가 실행 된 명령의 환경에 추가되고 현재 셸 환경에 영향을주지 않습니다.

이 변수 할당 기능을 사용하여 $IFS일시적으로 만 변경할 $OIFS있으므로 첫 번째 변형에서 변수 로 수행되는 것과 같은 전체 저장 및 복원 갬 빗을 피할 수 있습니다 . 그러나 여기서 직면하는 문제는 실행해야하는 명령 자체가 단순한 변수 할당이므로 $IFS할당을 임시 로 만드는 명령 단어를 포함하지 않는다는 것 입니다. 스스로 생각할 수 있습니다 : builtin. $IFS할당을 임시 로 만들기 위해 명령문에 no-op 명령 단어를 추가하지 않는 이유 무엇입니까? 이것은 $array할당을 임시로 만들기 때문에 작동하지 않습니다 .

IFS=', ' array=($countries) :; ## fails; new $array value never escapes the : command

그래서 우리는 사실상 곤경에 처해 있습니다. 그러나, eval그 코드를 실행, 그것은 정적 소스 코드를 정상적인 것처럼 그것은 쉘 환경에서 실행, 따라서 우리는 실행할 수있는 $array내부 할당 eval,이 쉘 환경에서 적용 갖고 인수를하면서 $IFS접두사 할당이 eval명령 앞에 붙은 것은 명령보다 오래 지속되지 않습니다 eval. 이것이 바로이 솔루션의 두 번째 변형에서 사용되는 트릭입니다.

IFS=', ' eval 'array=($string)'; ## $IFS does not outlive the eval command, but $array does

보시다시피, 이것은 실제로 꽤 영리한 트릭이며, (적어도 할당 효과와 관련하여) 필요한 것을 다소 명확하지 않은 방식으로 정확히 수행합니다. 의 관련에도 불구하고 저는 실제로이 트릭에 반대하지 않습니다 eval. 보안 위협으로부터 보호하기 위해 인수 문자열을 작은 따옴표로 묶어야합니다.

그러나 다시 말하지만, "모든 세계 중 최악의"문제의 집합체 때문에 이것은 여전히 ​​OP의 요구 사항에 대한 잘못된 대답입니다.


오답 # 6

IFS=', '; array=(Paris, France, Europe)

IFS=' ';declare -a array=(Paris France Europe)

음 ... 뭐? OP에는 배열로 구문 분석해야하는 문자열 변수가 있습니다. 이 "답변"은 배열 리터럴에 붙여 넣은 입력 문자열의 축어 내용으로 시작합니다. 그게 한 가지 방법이라고 생각합니다.

응답자는 $IFS변수가 모든 컨텍스트에서 모든 bash 구문 분석에 영향을 미친 다고 가정했을 수 있지만 이는 사실이 아닙니다. bash 매뉴얼에서 :

IFS     확장 후 단어 분할 및 read 내장 명령을 사용하여 행을 단어로 분할하는 데 사용되는 내부 필드 구분 기호입니다 . 기본값은 <space> <tab> <newline> 입니다.

따라서 $IFS특수 변수는 실제로 두 가지 컨텍스트에서만 사용됩니다. (1) 확장 후 수행되는 단어 분할 ( bash 소스 코드를 구문 분석 할 때가 아님 ) 및 (2) read내장에 의해 입력 행을 단어로 분할하는 데 사용 됩니다.

좀 더 명확하게하겠습니다. 파싱실행을 구분하는 것이 좋을 것 같습니다 . Bash는 먼저 파싱 이벤트 인 소스 코드를 먼저 파싱 한 다음 나중에 코드를 실행 합니다. 확장은 실제로 실행 이벤트입니다. 또한 위에서 인용 한 변수에 대한 설명에 문제가 있습니다 . 확장 후에 단어 분할이 수행된다고 말하는 것보다 확장 중에 단어 분할이 수행 되거나 더 정확하게는 단어 분할이 다음의 일부 라고 말할 수 있습니다.$IFS확장 과정. "단어 분리"라는 문구는이 확장 단계만을 의미합니다. 불행히도 문서에서 "분할"과 "단어"라는 단어를 많이 사용하는 것처럼 보이지만 bash 소스 코드의 구문 분석을 참조하는 데 사용해서는 안됩니다. 다음 은 bash 매뉴얼 linux.die.net 버전 에서 발췌 한 관련 내용입니다 .

확장은 단어로 분할 된 후 명령 줄에서 수행됩니다. 수행되는 확장 에는 중괄호 확장 , 물결표 확장 , 매개 변수 및 변수 확장 , 명령 대체 , 산술 확장 , 단어 분할경로 이름 확장의 7 가지 유형이 있습니다 .

확장 순서는 다음과 같습니다. 중괄호 확장; 물결표 확장, 매개 변수 및 변수 확장, 산술 확장 및 명령 대체 (왼쪽에서 오른쪽으로 수행) 단어 분할; 및 경로 이름 확장.

확장 섹션의 첫 번째 문장에서 "단어"대신 "토큰"이라는 단어를 선택하기 때문에 설명서 GNU 버전 이 약간 더 낫다고 주장 할 수 있습니다.

확장은 토큰으로 분할 된 후 명령 줄에서 수행됩니다.

중요한 점은 $IFSbash가 소스 코드를 구문 분석하는 방식을 변경하지 않는다는 것입니다. bash 소스 코드의 구문 분석은 실제로 명령 시퀀스, 명령 목록, 파이프 라인, 매개 변수 확장, 산술 대체 및 명령 대체와 같은 쉘 문법의 다양한 요소를 인식하는 매우 복잡한 프로세스입니다. 대부분의 경우 bash 구문 분석 프로세스는 변수 할당과 같은 사용자 수준 작업으로 변경할 수 없습니다 (실제로이 규칙에는 몇 가지 사소한 예외가 있습니다. 예를 들어 다양한 셸 설정 참조).compatxx, 즉석에서 구문 분석 동작의 특정 측면을 변경할 수 있습니다. 이 복잡한 구문 분석 프로세스의 결과 인 업스트림 "단어"/ "토큰"은 위의 문서 발췌 부분에서 분류 된 "확장"의 일반 프로세스에 따라 확장됩니다. 여기서 확장 된 (확장?) 텍스트를 다운 스트림으로 단어 분할 단어는 그 과정의 한 단계 일뿐입니다. 단어 분할은 이전 확장 단계에서 나온 텍스트에만 영향을줍니다. 소스 바이트 스트림에서 바로 구문 분석 된 리터럴 텍스트에는 영향을주지 않습니다.


오답 # 7

string='first line
        second line
        third line'

while read -r line; do lines+=("$line"); done <<<"$string"

이것은 최고의 솔루션 중 하나입니다. 다시 read. read우리가 하나만 필요로 할 때 두 단계의 분할을 수행하기 때문에 이전 에 부적절하다고 말하지 않았습니까 ? 여기서 트릭은 read특히 호출 당 하나의 필드 만 분할하여 효과적으로 한 수준의 분할 만 수행하는 방식으로 호출 할 수 있다는 것입니다. 약간의 수작업이지만 작동합니다.

그러나 문제가 있습니다. 첫째 :에 하나 이상의 NAME 인수를 제공 read하면 입력 문자열에서 분리 된 각 필드의 선행 및 후행 공백이 자동으로 무시됩니다. $IFS게시물의 앞부분에서 설명한대로이 기본값으로 설정되어 있는지 여부에 관계없이 발생합니다 . 이제 OP는 그의 특정 사용 사례에 대해 이것을 신경 쓰지 않을 수 있으며 실제로는 구문 분석 동작의 바람직한 기능 일 수 있습니다. 그러나 문자열을 필드로 구문 분석하려는 모든 사람이 이것을 원하는 것은 아닙니다. 그러나 해결책이 있습니다.의 다소 분명하지 않은 사용법은 read0 NAME 인수 를 전달하는 것 입니다. 이 경우 read는 입력 스트림에서 가져온 전체 입력 행을라는 변수에 저장 $REPLY하며 보너스로 저장하지 않습니다.값에서 선행 및 후행 공백을 제거합니다. 이것은 read쉘 프로그래밍 경력에서 자주 사용하는 매우 강력한 사용법입니다 . 다음은 동작의 차이를 보여줍니다.

string=$'  a  b  \n  c  d  \n  e  f  '; ## input string

a=(); while read -r line; do a+=("$line"); done <<<"$string"; declare -p a;
## declare -a a=([0]="a  b" [1]="c  d" [2]="e  f") ## read trimmed surrounding whitespace

a=(); while read -r; do a+=("$REPLY"); done <<<"$string"; declare -p a;
## declare -a a=([0]="  a  b  " [1]="  c  d  " [2]="  e  f  ") ## no trimming

이 솔루션의 두 번째 문제는 OP의 쉼표 공간과 같은 사용자 지정 필드 구분 기호의 경우를 실제로 해결하지 않는다는 것입니다. 이전과 마찬가지로 다중 문자 구분 기호는 지원되지 않으며 이는이 솔루션의 불행한 제한입니다. -d옵션에 구분 기호를 지정하여 최소한 쉼표로 분할하려고 할 수 있지만 어떤 일이 발생하는지 살펴보십시오.

string='Paris, France, Europe';
a=(); while read -rd,; do a+=("$REPLY"); done <<<"$string"; declare -p a;
## declare -a a=([0]="Paris" [1]=" France")

예상대로 설명되지 않은 주변 공백이 필드 값으로 가져 와서 트리밍 작업을 통해 이후에 수정해야합니다 (while 루프에서 직접 수행 할 수도 있음). 그러나 또 다른 명백한 오류가 있습니다. 유럽이 없습니다! 무슨 일이 있었나요? 대답은 read최종 필드에서 최종 필드 종결자를 만나지 않고 파일 끝 (이 경우 문자열 끝이라고 부를 수 있음)에 도달하면 실패한 반환 코드 반환 한다는 것입니다. 이로 인해 while 루프가 조기에 중단되고 최종 필드가 손실됩니다.

기술적으로이 동일한 오류는 이전 예제에도 영향을 미쳤습니다. 차이점은 필드 구분 기호가 -d옵션을 지정하지 않을 때 기본값 인 LF로 간주 되고 <<<( "here-string") 메커니즘이 자동으로 문자열에 LF를 추가한다는 점입니다. 명령에 대한 입력. 따라서 이러한 경우에 우리는 실수로 추가 더미 터미네이터를 입력에 추가하여 최종 필드가 떨어지는 문제 실수로 해결했습니다. 이 솔루션을 "더미 터미네이터"솔루션이라고합시다. here-string에서 인스턴스화 할 때 입력 문자열에 대해 직접 연결하여 사용자 지정 구분 기호에 대해 더미 종료 자 솔루션을 수동으로 적용 할 수 있습니다.

a=(); while read -rd,; do a+=("$REPLY"); done <<<"$string,"; declare -p a;
declare -a a=([0]="Paris" [1]=" France" [2]=" Europe")

문제가 해결되었습니다. 또 다른 해결책은 (1) read오류를 반환하고 (2) $REPLY가 비어있는 경우에만 while 루프를 중단하는 것 입니다. 즉, read파일 끝을 누르기 전에 문자를 읽을 수 없었 음을 의미 합니다. 데모:

a=(); while read -rd,|| [[ -n "$REPLY" ]]; do a+=("$REPLY"); done <<<"$string"; declare -p a;
## declare -a a=([0]="Paris" [1]=" France" [2]=$' Europe\n')

이 접근 방식은 또한 <<<리디렉션 연산자에 의해 here-string에 자동으로 추가되는 비밀 LF를 보여줍니다 . 물론 조금 전에 설명한 것처럼 명시적인 트리밍 작업을 통해 개별적으로 제거 할 수 있지만 분명히 수동 더미 터미네이터 접근 방식이이를 직접 해결하므로 그대로 사용할 수 있습니다. 수동 더미 터미네이터 솔루션은이 두 가지 문제 (Drop-final-field 문제와 추가 된 LF 문제)를 한 번에 해결한다는 점에서 실제로 매우 편리합니다.

따라서 전반적으로 이것은 매우 강력한 솔루션입니다. 유일한 약점은 나중에 다룰 다중 문자 구분 기호에 대한 지원이 없다는 것입니다.


오답 # 8

string='first line
        second line
        third line'

readarray -t lines <<<"$string"

(이것은 실제로 # 7 과 동일한 게시물에서 가져온 것입니다 . 답변자는 동일한 게시물에서 두 가지 솔루션을 제공했습니다.)

readarray동의어 인 내장 mapfile이 이상적입니다. 한 번에 바이트 스트림을 배열 변수로 구문 분석하는 내장 명령입니다. 루프, 조건부, 대체 또는 다른 것을 망칠 필요가 없습니다. 그리고 입력 문자열에서 공백을 은밀하게 제거하지 않습니다. 그리고 ( -O주지 않은 경우 ) 할당하기 전에 대상 배열을 편리하게 지 웁니다. 그러나 그것은 여전히 ​​완벽하지 않기 때문에 "오답"이라고 비판합니다.

먼저,이 작업을 방해하기 위해 read필드 구문 분석을 수행 할 때 의 동작과 마찬가지로 readarray후행 필드가 비어 있으면 삭제합니다. 다시 말하지만 이것은 아마도 OP에 대한 관심사는 아니지만 일부 사용 사례에 대한 것일 수 있습니다. 잠시 후에 다시 설명하겠습니다.

둘째, 이전과 마찬가지로 다중 문자 구분 기호를 지원하지 않습니다. 이 문제도 잠시 후에 수정하겠습니다.

셋째, 작성된 솔루션은 OP의 입력 문자열을 구문 분석하지 않으며 실제로 구문 분석에 그대로 사용할 수 없습니다. 이것에 대해서도 잠시 설명하겠습니다.

위의 이유로 나는 여전히 이것이 OP의 질문에 대한 "잘못된 대답"이라고 생각합니다. 아래에서 내가 정답이라고 생각하는 것을 줄 것입니다.


올바른 대답

다음 옵션을 지정하여 # 8을 작동 시키려는 순진한 시도입니다 -d.

string='Paris, France, Europe';
readarray -td, a <<<"$string"; declare -p a;
## declare -a a=([0]="Paris" [1]=" France" [2]=$' Europe\n')

결과는 # 7read 에서 논의 된 루핑 솔루션 의 이중 조건부 접근법에서 얻은 결과와 동일합니다 . 수동 더미 터미네이터 트릭으로이 문제 거의 해결할 수 있습니다 .

readarray -td, a <<<"$string,"; declare -p a;
## declare -a a=([0]="Paris" [1]=" France" [2]=" Europe" [3]=$'\n')

여기서 문제 readarray<<<리디렉션 연산자가 LF를 입력 문자열에 추가 했기 때문에 후행 필드 보존 했기 때문에 후행 필드가 비어 있지 않았습니다 (그렇지 않으면 삭제되었을 것입니다). 사후 최종 배열 요소를 명시 적으로 설정 해제하여이를 처리 할 수 ​​있습니다.

readarray -td, a <<<"$string,"; unset 'a[-1]'; declare -p a;
## declare -a a=([0]="Paris" [1]=" France" [2]=" Europe")

실제로 관련된 유일한 두 가지 문제는 (1) 제거해야하는 외부 공백과 (2) 다중 문자 구분 기호에 대한 지원 부족입니다.

물론 공백은 나중에 잘라낼 수 있습니다 (예 : Bash 변수에서 공백을 잘라내방법? ). 그러나 다중 문자 구분 기호를 해킹 할 수 있다면 두 문제를 한 번에 해결할 수 있습니다.

안타깝게도 여러 문자 구분 기호를 작동시키는 직접적인 방법 은 없습니다 . 내가 생각한 가장 좋은 해결책은 입력 문자열을 사전 처리하여 다중 문자 구분 기호를 입력 문자열의 내용과 충돌하지 않도록 보장되는 단일 문자 구분 기호로 바꾸는 것입니다. 이 보증을 가진 유일한 문자는 NUL 바이트 입니다. 이는 bash (zsh는 아니지만)에서는 변수가 NUL 바이트를 포함 할 수 없기 때문입니다. 이 전처리 단계는 프로세스 대체에서 인라인으로 수행 될 수 있습니다. 다음은 awk를 사용하여 수행하는 방법입니다 .

readarray -td '' a < <(awk '{ gsub(/, /,"\0"); print; }' <<<"$string, "); unset 'a[-1]';
declare -p a;
## declare -a a=([0]="Paris" [1]="France" [2]="Europe")

드디어! 이 솔루션은 중간에있는 필드를 잘못 분할하지 않고, 너무 일찍 잘라 내지 않으며, 빈 필드를 삭제하지 않으며, 파일 이름 확장시 자체적으로 손상되지 않으며, 선행 및 후행 공백을 자동으로 제거하지 않으며 끝 부분에 스토 웨이 LF를 남기지 않습니다. 루프가 필요하지 않으며 단일 문자 구분 기호로 해결되지 않습니다.


트리밍 솔루션

마지막으로 모호한 -C callback옵션을 사용하여 상당히 복잡한 트리밍 솔루션을 보여주고 싶었습니다 readarray. 안타깝게도 Stack Overflow의 획기적인 30,000 자 게시 제한에 대한 공간이 부족하여 설명 할 수 없습니다. 독자를위한 연습 문제로 남겨 두겠습니다.

function mfcb { local val="$4"; "$1"; eval "$2[$3]=\$val;"; };
function val_ltrim { if [[ "$val" =~ ^[[:space:]]+ ]]; then val="${val:${#BASH_REMATCH[0]}}"; fi; };
function val_rtrim { if [[ "$val" =~ [[:space:]]+$ ]]; then val="${val:0:${#val}-${#BASH_REMATCH[0]}}"; fi; };
function val_trim { val_ltrim; val_rtrim; };
readarray -c1 -C 'mfcb val_trim a' -td, <<<"$string,"; unset 'a[-1]'; declare -p a;
## declare -a a=([0]="Paris" [1]="France" [2]="Europe")

다음은 IFS를 설정하지 않는 방법입니다.

string="1:2:3:4:5"
set -f                      # avoid globbing (expansion of *).
array=(${string//:/ })
for i in "${!array[@]}"
do
    echo "$i=>${array[i]}"
done

아이디어는 문자열 교체를 사용하는 것입니다.

${string//substring/replacement}

$ substring의 모든 일치 항목을 공백으로 바꾼 다음 대체 된 문자열을 사용하여 배열을 초기화합니다.

(element1 element2 ... elementN)

참고 :이 답변은 split + glob 연산자를 사용 합니다. 따라서 일부 문자 (예 :)의 확장을 방지 *하려면이 스크립트에 대해 globbing을 일시 중지하는 것이 좋습니다.


t="one,two,three"
a=($(echo "$t" | tr ',' '\n'))
echo "${a[2]}"

3 장 인쇄


때로는 수락 된 답변에 설명 된 방법이 작동하지 않는 경우가 있습니다. 특히 구분 기호가 캐리지 리턴 인 경우.
이 경우 다음과 같이 해결했습니다.

string='first line
second line
third line'

oldIFS="$IFS"
IFS='
'
IFS=${IFS:0:1} # this is useful to format your code with tabs
lines=( $string )
IFS="$oldIFS"

for line in "${lines[@]}"
    do
        echo "--> $line"
done

허용되는 답변은 한 줄의 값에 적용됩니다.
변수에 여러 줄이있는 경우 :

string='first line
        second line
        third line'

모든 줄을 얻으려면 매우 다른 명령이 필요합니다.

while read -r line; do lines+=("$line"); done <<<"$string"

또는 훨씬 더 간단한 bash readarray :

readarray -t lines <<<"$string"

모든 줄을 인쇄하는 것은 printf 기능을 활용하여 매우 쉽습니다.

printf ">[%s]\n" "${lines[@]}"

>[first line]
>[        second line]
>[        third line]

문자열을 배열로 분할하는 핵심은의 다중 문자 구분 기호입니다 ", ". IFS다중 문자 구분 기호에 사용 하는 모든 솔루션 은 IFS가 문자열이 아닌 해당 문자 집합이므로 본질적으로 잘못되었습니다.

할당 IFS=", "하는 경우 문자열은 의 두 문자 구분 기호를 정확하게 표현하지 않는 ","OR " "또는 이들의 조합에서 중단됩니다 ", ".

awk또는 sed사용 하여 프로세스 대체를 통해 문자열을 분할 할 수 있습니다 .

#!/bin/bash

str="Paris, France, Europe"
array=()
while read -r -d $'\0' each; do   # use a NUL terminated field separator 
    array+=("$each")
done < <(printf "%s" "$str" | awk '{ gsub(/,[ ]+|$/,"\0"); print }')
declare -p array
# declare -a array=([0]="Paris" [1]="France" [2]="Europe") output

Bash에서 직접 정규식을 사용하는 것이 더 효율적입니다.

#!/bin/bash

str="Paris, France, Europe"

array=()
while [[ $str =~ ([^,]+)(,[ ]+|$) ]]; do
    array+=("${BASH_REMATCH[1]}")   # capture the field
    i=${#BASH_REMATCH}              # length of field + delimiter
    str=${str:i}                    # advance the string by that length
done                                # the loop deletes $str, so make a copy if needed

declare -p array
# declare -a array=([0]="Paris" [1]="France" [2]="Europe") output...

두 번째 형식에서는 하위 셸이 없으며 본질적으로 더 빠릅니다.


bgoldst 편집 : 다음은 내 readarray솔루션을 dawg 의 정규식 솔루션 과 비교하는 몇 가지 벤치 마크 이며, 이에 대한 read솔루션 도 포함 했습니다 (참고 : 내 솔루션과의 조화를 위해 정규식 솔루션을 약간 수정했습니다). 우편):

## competitors
function c_readarray { readarray -td '' a < <(awk '{ gsub(/, /,"\0"); print; };' <<<"$1, "); unset 'a[-1]'; };
function c_read { a=(); local REPLY=''; while read -r -d ''; do a+=("$REPLY"); done < <(awk '{ gsub(/, /,"\0"); print; };' <<<"$1, "); };
function c_regex { a=(); local s="$1, "; while [[ $s =~ ([^,]+),\  ]]; do a+=("${BASH_REMATCH[1]}"); s=${s:${#BASH_REMATCH}}; done; };

## helper functions
function rep {
    local -i i=-1;
    for ((i = 0; i<$1; ++i)); do
        printf %s "$2";
    done;
}; ## end rep()

function testAll {
    local funcs=();
    local args=();
    local func='';
    local -i rc=-1;
    while [[ "$1" != ':' ]]; do
        func="$1";
        if [[ ! "$func" =~ ^[_a-zA-Z][_a-zA-Z0-9]*$ ]]; then
            echo "bad function name: $func" >&2;
            return 2;
        fi;
        funcs+=("$func");
        shift;
    done;
    shift;
    args=("$@");
    for func in "${funcs[@]}"; do
        echo -n "$func ";
        { time $func "${args[@]}" >/dev/null 2>&1; } 2>&1| tr '\n' '/';
        rc=${PIPESTATUS[0]}; if [[ $rc -ne 0 ]]; then echo "[$rc]"; else echo; fi;
    done| column -ts/;
}; ## end testAll()

function makeStringToSplit {
    local -i n=$1; ## number of fields
    if [[ $n -lt 0 ]]; then echo "bad field count: $n" >&2; return 2; fi;
    if [[ $n -eq 0 ]]; then
        echo;
    elif [[ $n -eq 1 ]]; then
        echo 'first field';
    elif [[ "$n" -eq 2 ]]; then
        echo 'first field, last field';
    else
        echo "first field, $(rep $[$1-2] 'mid field, ')last field";
    fi;
}; ## end makeStringToSplit()

function testAll_splitIntoArray {
    local -i n=$1; ## number of fields in input string
    local s='';
    echo "===== $n field$(if [[ $n -ne 1 ]]; then echo 's'; fi;) =====";
    s="$(makeStringToSplit "$n")";
    testAll c_readarray c_read c_regex : "$s";
}; ## end testAll_splitIntoArray()

## results
testAll_splitIntoArray 1;
## ===== 1 field =====
## c_readarray   real  0m0.067s   user 0m0.000s   sys  0m0.000s
## c_read        real  0m0.064s   user 0m0.000s   sys  0m0.000s
## c_regex       real  0m0.000s   user 0m0.000s   sys  0m0.000s
##
testAll_splitIntoArray 10;
## ===== 10 fields =====
## c_readarray   real  0m0.067s   user 0m0.000s   sys  0m0.000s
## c_read        real  0m0.064s   user 0m0.000s   sys  0m0.000s
## c_regex       real  0m0.001s   user 0m0.000s   sys  0m0.000s
##
testAll_splitIntoArray 100;
## ===== 100 fields =====
## c_readarray   real  0m0.069s   user 0m0.000s   sys  0m0.062s
## c_read        real  0m0.065s   user 0m0.000s   sys  0m0.046s
## c_regex       real  0m0.005s   user 0m0.000s   sys  0m0.000s
##
testAll_splitIntoArray 1000;
## ===== 1000 fields =====
## c_readarray   real  0m0.084s   user 0m0.031s   sys  0m0.077s
## c_read        real  0m0.092s   user 0m0.031s   sys  0m0.046s
## c_regex       real  0m0.125s   user 0m0.125s   sys  0m0.000s
##
testAll_splitIntoArray 10000;
## ===== 10000 fields =====
## c_readarray   real  0m0.209s   user 0m0.093s   sys  0m0.108s
## c_read        real  0m0.333s   user 0m0.234s   sys  0m0.109s
## c_regex       real  0m9.095s   user 0m9.078s   sys  0m0.000s
##
testAll_splitIntoArray 100000;
## ===== 100000 fields =====
## c_readarray   real  0m1.460s   user 0m0.326s   sys  0m1.124s
## c_read        real  0m2.780s   user 0m1.686s   sys  0m1.092s
## c_regex       real  17m38.208s   user 15m16.359s   sys  2m19.375s
##

이것은 Jmoney38접근 방식 과 유사 하지만 sed를 사용합니다.

string="1,2,3,4"
array=(`echo $string | sed 's/,/\n/g'`)
echo ${array[0]}

인쇄 1


순수한 bash 다중 문자 구분 기호 솔루션.

다른 사람들이이 스레드에서 지적했듯이 OP의 질문은 배열로 구문 분석 될 쉼표로 구분 된 문자열의 예를 제공했지만 쉼표 구분 기호, 단일 문자 구분 기호 또는 다중 문자에만 관심이 있는지 여부는 나타내지 않았습니다. 구분자.

Google은이 답변의 순위를 검색 결과의 상단 또는 그 근처에 배치하는 경향이 있기 때문에 독자들에게 여러 문자 구분 기호에 대한 강력한 답변을 제공하고 싶었습니다.

다중 문자 구분 기호 문제에 대한 해결책을 찾고 있다면 Mallikarjun M 의 게시물, 특히 매개 변수 확장을 사용하여이 우아한 순수 BASH 솔루션을 제공 하는 gniourf_gniourf 의 응답을 검토하는 것이 좋습니다 .

#!/bin/bash
str="LearnABCtoABCSplitABCaABCString"
delimiter=ABC
s=$str$delimiter
array=();
while [[ $s ]]; do
    array+=( "${s%%"$delimiter"*}" );
    s=${s#*"$delimiter"};
done;
declare -p array

인용 된 댓글 / 참조 된 게시물 링크

인용 된 질문에 대한 링크 : bash에서 다중 문자 구분 기호로 문자열을 분할하는 방법은 무엇입니까?


이 시도

IFS=', '; array=(Paris, France, Europe)
for item in ${array[@]}; do echo $item; done

간단 해. 원하는 경우 선언을 추가하고 쉼표를 제거 할 수도 있습니다.

IFS=' ';declare -a array=(Paris France Europe)

위의 실행을 취소하기 위해 IFS가 추가되었지만 새로운 bash 인스턴스에서 작동하지 않습니다.


IFS를 수정하지 않고 수행하는 또 다른 방법 :

read -r -a myarray <<< "${string//, /$IFS}"

원하는 구분 기호와 일치하도록 IFS를 변경하는 대신 원하는 구분 기호의 모든 항목 ", "$IFSvia 내용으로 바꿀 수 있습니다 "${string//, /$IFS}".

아마도 이것은 매우 큰 문자열의 경우 느릴 것입니까?

이것은 Dennis Williamson의 답변을 기반으로합니다.


다음과 같은 입력을 구문 분석 할 때이 게시물을 보았습니다 : word1, word2, ...

위의 어느 것도 나를 도왔습니다. awk를 사용하여 해결했습니다. 누군가에게 도움이되는 경우 :

STRING="value1,value2,value3"
array=`echo $STRING | awk -F ',' '{ s = $1; for (i = 2; i <= NF; i++) s = s "\n"$i; print s; }'`
for word in ${array}
do
        echo "This is the word $word"
done

이것은 OSX에서 나를 위해 작동합니다.

string="1 2 3 4 5"
declare -a array=($string)

문자열에 다른 구분 기호가 있으면 먼저 공백으로 바꿉니다.

string="1,2,3,4,5"
delimiter=","
declare -a array=($(echo $string | tr "$delimiter" " "))

단순 :-)


이것을 사용하십시오 :

countries='Paris, France, Europe'
OIFS="$IFS"
IFS=', ' array=($countries)
IFS="$OIFS"

#${array[1]} == Paris
#${array[2]} == France
#${array[3]} == Europe

업데이트 : eval 문제로 인해 이렇게하지 마십시오.

약간 덜 의식 :

IFS=', ' eval 'array=($string)'

예 :

string="foo, bar,baz"
IFS=', ' eval 'array=($string)'
echo ${array[1]} # -> bar

여기 내 해킹이 있습니다!

bash를 사용하여 문자열을 문자열로 분할하는 것은 꽤 지루한 일입니다. 발생하는 것은 몇 가지 경우 ( ";", "/", "."등으로 분할)에서만 작동하는 접근 방식이 제한적이거나 출력에 다양한 부작용이 있다는 것입니다.

아래의 접근 방식에는 여러 가지 조작이 필요했지만 대부분의 요구 사항에 적합 할 것이라고 생각합니다!

#!/bin/bash

# --------------------------------------
# SPLIT FUNCTION
# ----------------

F_SPLIT_R=()
f_split() {
    : 'It does a "split" into a given string and returns an array.

    Args:
        TARGET_P (str): Target string to "split".
        DELIMITER_P (Optional[str]): Delimiter used to "split". If not 
    informed the split will be done by spaces.

    Returns:
        F_SPLIT_R (array): Array with the provided string separated by the 
    informed delimiter.
    '

    F_SPLIT_R=()
    TARGET_P=$1
    DELIMITER_P=$2
    if [ -z "$DELIMITER_P" ] ; then
        DELIMITER_P=" "
    fi

    REMOVE_N=1
    if [ "$DELIMITER_P" == "\n" ] ; then
        REMOVE_N=0
    fi

    # NOTE: This was the only parameter that has been a problem so far! 
    # By Questor
    # [Ref.: https://unix.stackexchange.com/a/390732/61742]
    if [ "$DELIMITER_P" == "./" ] ; then
        DELIMITER_P="[.]/"
    fi

    if [ ${REMOVE_N} -eq 1 ] ; then

        # NOTE: Due to bash limitations we have some problems getting the 
        # output of a split by awk inside an array and so we need to use 
        # "line break" (\n) to succeed. Seen this, we remove the line breaks 
        # momentarily afterwards we reintegrate them. The problem is that if 
        # there is a line break in the "string" informed, this line break will 
        # be lost, that is, it is erroneously removed in the output! 
        # By Questor
        TARGET_P=$(awk 'BEGIN {RS="dn"} {gsub("\n", "3F2C417D448C46918289218B7337FCAF"); printf $0}' <<< "${TARGET_P}")

    fi

    # NOTE: The replace of "\n" by "3F2C417D448C46918289218B7337FCAF" results 
    # in more occurrences of "3F2C417D448C46918289218B7337FCAF" than the 
    # amount of "\n" that there was originally in the string (one more 
    # occurrence at the end of the string)! We can not explain the reason for 
    # this side effect. The line below corrects this problem! By Questor
    TARGET_P=${TARGET_P%????????????????????????????????}

    SPLIT_NOW=$(awk -F"$DELIMITER_P" '{for(i=1; i<=NF; i++){printf "%s\n", $i}}' <<< "${TARGET_P}")

    while IFS= read -r LINE_NOW ; do
        if [ ${REMOVE_N} -eq 1 ] ; then

            # NOTE: We use "'" to prevent blank lines with no other characters 
            # in the sequence being erroneously removed! We do not know the 
            # reason for this side effect! By Questor
            LN_NOW_WITH_N=$(awk 'BEGIN {RS="dn"} {gsub("3F2C417D448C46918289218B7337FCAF", "\n"); printf $0}' <<< "'${LINE_NOW}'")

            # NOTE: We use the commands below to revert the intervention made 
            # immediately above! By Questor
            LN_NOW_WITH_N=${LN_NOW_WITH_N%?}
            LN_NOW_WITH_N=${LN_NOW_WITH_N#?}

            F_SPLIT_R+=("$LN_NOW_WITH_N")
        else
            F_SPLIT_R+=("$LINE_NOW")
        fi
    done <<< "$SPLIT_NOW"
}

# --------------------------------------
# HOW TO USE
# ----------------

STRING_TO_SPLIT="
 * How do I list all databases and tables using psql?

\"
sudo -u postgres /usr/pgsql-9.4/bin/psql -c \"\l\"
sudo -u postgres /usr/pgsql-9.4/bin/psql <DB_NAME> -c \"\dt\"
\"

\"
\list or \l: list all databases
\dt: list all tables in the current database
\"

[Ref.: https://dba.stackexchange.com/questions/1285/how-do-i-list-all-databases-and-tables-using-psql]


"

f_split "$STRING_TO_SPLIT" "bin/psql -c"

# --------------------------------------
# OUTPUT AND TEST
# ----------------

ARR_LENGTH=${#F_SPLIT_R[*]}
for (( i=0; i<=$(( $ARR_LENGTH -1 )); i++ )) ; do
    echo " > -----------------------------------------"
    echo "${F_SPLIT_R[$i]}"
    echo " < -----------------------------------------"
done

if [ "$STRING_TO_SPLIT" == "${F_SPLIT_R[0]}bin/psql -c${F_SPLIT_R[1]}" ] ; then
    echo " > -----------------------------------------"
    echo "The strings are the same!"
    echo " < -----------------------------------------"
fi

또 다른 방법은 다음과 같습니다.

string="Paris, France, Europe"
IFS=', ' arr=(${string})

이제 요소가 "arr"배열에 저장됩니다. 요소를 반복하려면 :

for i in ${arr[@]}; do echo $i; done

또 다른 접근 방식은 다음과 같습니다.

str="a, b, c, d"  # assuming there is a space after ',' as in Q
arr=(${str//,/})  # delete all occurrences of ','

이 'arr'뒤에는 4 개의 문자열이있는 배열이 있습니다. 이것은 IFS를 다루거나 읽거나 다른 특별한 것들을 요구하지 않으므로 훨씬 간단하고 직접적입니다.

참고 URL : https://stackoverflow.com/questions/10586153/split-string-into-an-array-in-bash

반응형