etc./StackOverFlow

Bash의 구분 기호에서 문자열을 어떻게 분할합니까?

청렴결백한 만능 재주꾼 2021. 11. 29. 22:34
반응형

질문자 :stefanB


이 문자열을 변수에 저장했습니다.

 IN="bla@some.com;john@home.com"

이제 문자열을 다음으로 나누고 싶습니다 ; 내가 가질 수 있도록 구분 기호 :

 ADDR1="bla@some.com" ADDR2="john@home.com"

ADDR1ADDR2 변수가 반드시 필요한 것은 아닙니다. 그것들이 배열의 요소라면 더욱 좋습니다.


아래 답변에서 제안한 후 내가 추구한 바는 다음과 같습니다.

 #!/usr/bin/env bash IN="bla@some.com;john@home.com" mails=$(echo $IN | tr ";" "\n") for addr in $mails do echo "> [$addr]" done

산출:

 > [bla@some.com] > [john@home.com]

Internal_field_separator (IFS)를 다음으로 설정하는 것과 관련된 솔루션이 있었습니다 ; . 그 대답에 무슨 일이 일어났는지 잘 모르겠습니다. 어떻게 IFS 를 기본값으로 다시 재설정합니까?

RE: IFS 솔루션, 이것을 시도했는데 작동합니다. 이전 IFS 유지한 다음 복원합니다.

 IN="bla@some.com;john@home.com" OIFS=$IFS IFS=';' mails2=$IN for x in $mails2 do echo "> [$x]" done IFS=$OIFS

BTW, 내가 시도했을 때

 mails2=($IN)

루프에서 인쇄할 때 첫 번째 문자열만 얻었습니다 $IN 주위에 대괄호가 없으면 작동합니다.



내부 필드 구분 기호 (IFS) 변수를 설정한 다음 배열로 구문 분석할 수 있습니다. 이것이 명령에서 발생하면 IFS 대한 할당은 해당 단일 명령의 환경( read )에만 발생합니다. IFS 변수 값에 따라 입력을 배열로 구문 분석한 다음 반복할 수 있습니다.

이 예제는 ; , 배열로 푸시:

 IFS=';' read -ra ADDR <<< "$IN" for i in "${ADDR[@]}"; do # process "$i" done

$IN 의 전체 내용을 처리하기 위한 것입니다. 매번 한 줄의 입력이 으로 구분됩니다 ; :

 while IFS=';' read -ra ADDR; do for i in "${ADDR[@]}"; do # process "$i" done done <<< "$IN"

Johannes Schaub - litb

Bash 쉘 스크립트 분할 배열 에서 가져옴:

 IN="bla@some.com;john@home.com" arrIN=(${IN//;/ }) echo ${arrIN[1]} # Output: john@home.com

설명:

';' 의 모든 발생을 대체합니다. (초기 // 는 전역 대체를 의미함) ' ' IN 에서 공백으로 구분된 문자열을 배열로 해석합니다(주변 괄호가 수행하는 작업).

';' 를 대체하기 위해 중괄호 내부에 사용되는 구문 ' ' 문자가 있는 문자를 매개변수 확장 이라고 합니다.

몇 가지 일반적인 문제가 있습니다.

  1. 원래 문자열에 공백이 있으면 IFS 를 사용해야 합니다.
  • IFS=':'; arrIN=($IN); unset IFS;
  1. 원래 문자열에 공백이 있고 구분 기호가 새 줄이면 다음을 사용 하여 IFS를 설정할 수 있습니다.
  • IFS=$'\n'; arrIN=($IN); unset IFS;

palindrom

즉시 처리하는 것이 마음에 들지 않는다면 다음과 같이 하십시오.

 for i in $(echo $IN | tr ";" "\n") do # process done

이런 종류의 루프를 사용하여 배열을 초기화할 수 있지만 아마도 더 쉬운 방법이 있을 것입니다. 그래도 도움이 되기를 바랍니다.


Chris Lutz

호환 가능한 답변

에서 이를 수행하는 다양한 방법이 있습니다.

bash 에는 에서는 작동하지 않는 많은 특수 기능(소위 bashisms )이 있다는 점을 먼저 알아두는 것이 중요합니다.

특히 이 게시물의 솔루션과 스레드의 다른 솔루션에 사용되는 arrays , associative arrayspattern replacement 는 bashism이며 많은 사람들이 사용하는 다른 쉘 에서는 작동하지 않을 수 있습니다.

예를 들어: 내 Debian GNU/Linux 에는 라는 표준 셸이 있습니다. 라는 다른 쉘을 사용하는 것을 좋아하는 많은 사람들을 알고 있습니다. 그리고 자신의 쉘 인터프리터( )가 있는 라는 특수 도구도 있습니다.

요청한 문자열

위의 질문에서 분할할 문자열은 다음과 같습니다.

 IN="bla@some.com;john@home.com"

이 문자열의 수정된 버전을 사용하여 내 솔루션이 다른 솔루션을 손상시킬 수 있는 공백이 포함된 문자열에 대해 강력한지 확인합니다.

 IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"

구분 기호를 기반으로 문자열 분할(버전 >=4.2)

순수한 bash 에서는 IFS ( 입력 필드 구분 기호 )에 대한 임시 값으로 분할된 요소가 있는 배열 을 만들 수 있습니다. IFS는 무엇보다도 배열을 정의할 때 요소 사이의 구분 기호로 처리해야 하는 문자를 bash

 IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>" # save original IFS value so we can restore it later oIFS="$IFS" IFS=";" declare -a fields=($IN) IFS="$oIFS" unset oIFS

최신 버전의 bash 에서 명령 앞에 IFS 정의를 추가하면 해당 명령에 대한 IFS 변경되고 직후에 이전 값으로 재설정됩니다. 즉, 위의 작업을 한 줄로 수행할 수 있습니다.

 IFS=\; read -a fields <<<"$IN" # after this command, the IFS resets back to its previous value (here, the default): set | grep ^IFS= # IFS=$' \t\n'

IN 이 세미콜론으로 분할된 fields 배열에 저장되었음을 알 수 있습니다.

 set | grep ^fields=\\\|^IN= # fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>") # IN='bla@some.com;john@home.com;Full Name <fulnam@other.org>'

declare -p 사용하여 이러한 변수의 내용을 표시할 수도 있습니다. :)

 declare -p IN fields # declare -- IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>" # declare -a fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>")

호출되는 포크 나 외부 리소스가 없기 때문에 read분할을 수행하는 가장 빠른 방법입니다.

배열이 정의되면 간단한 루프를 사용하여 각 필드(또는 지금 정의한 배열의 각 요소)를 처리할 수 있습니다.

 # `"${fields[@]}"` expands to return every element of `fields` array as a separate argument for x in "${fields[@]}" ;do echo "> [$x]" done # > [bla@some.com] # > [john@home.com] # > [Full Name <fulnam@other.org>]

또는 내가 좋아 하는 이동 방식을 사용하여 처리한 후 배열에서 각 필드를 삭제할 수 있습니다.

 while [ "$fields" ] ;do echo "> [$fields]" # slice the array fields=("${fields[@]:1}") done # > [bla@some.com] # > [john@home.com] # > [Full Name <fulnam@other.org>]

그리고 단순히 배열을 출력하고 싶다면 반복할 필요도 없습니다.

 printf "> [%s]\n" "${fields[@]}" # > [bla@some.com] # > [john@home.com] # > [Full Name <fulnam@other.org>]

업데이트: 최근 >= 4.4

최신 버전의 bash mapfile 명령으로 재생할 수도 있습니다.

 mapfile -td \; fields < <(printf "%s\0" "$IN")

이 구문은 특수 문자, 줄 바꿈 및 빈 필드를 보존합니다!

빈 필드를 포함하지 않으려면 다음을 수행할 수 있습니다.

 mapfile -td \; fields <<<"$IN" fields=("${fields[@]%$'\n'}") # drop '\n' added by '<<<'

mapfile 사용하면 배열 선언을 건너뛰고 구분된 요소에 대해 암시적으로 "루프"하여 각각에 대해 함수를 호출할 수도 있습니다.

 myPubliMail() { printf "Seq: %6d: Sending mail to '%s'..." $1 "$2" # mail -s "This is not a spam..." "$2" </path/to/body printf "\e[3D, done.\n" } mapfile < <(printf "%s\0" "$IN") -td \; -c 1 -C myPubliMail

(참고: \0 은 문자열 끝에 있는 빈 필드에 신경 쓰지 않거나 존재하지 않는 경우 쓸모가 없습니다.)

 mapfile < <(echo -n "$IN") -td \; -c 1 -C myPubliMail # Seq: 0: Sending mail to 'bla@some.com', done. # Seq: 1: Sending mail to 'john@home.com', done. # Seq: 2: Sending mail to 'Full Name <fulnam@other.org>', done.

또는 <<< 사용할 수 있고 함수 본문에 추가하는 개행을 삭제하는 처리를 포함할 수 있습니다.

 myPubliMail() { local seq=$1 dest="${2%$'\n'}" printf "Seq: %6d: Sending mail to '%s'..." $seq "$dest" # mail -s "This is not a spam..." "$dest" </path/to/body printf "\e[3D, done.\n" } mapfile <<<"$IN" -td \; -c 1 -C myPubliMail # Renders the same output: # Seq: 0: Sending mail to 'bla@some.com', done. # Seq: 1: Sending mail to 'john@home.com', done. # Seq: 2: Sending mail to 'Full Name <fulnam@other.org>', done.

구분 기호를 기반으로 문자열 분할

bash 사용할 수 없거나 다양한 쉘에서 사용할 수 있는 것을 작성하려는 경우 bashism을 사용할 수 없는 경우가 많습니다. 여기에는 위의 솔루션에서 사용한 배열도 포함됩니다.

그러나 문자열의 "요소"를 반복하기 위해 배열을 사용할 필요는 없습니다. 패턴 의 첫 번째 또는 마지막 발생에서 문자열의 하위 문자열을 삭제하기 위해 많은 쉘에서 사용되는 구문이 있습니다. * 는 0개 이상의 문자를 나타내는 와일드카드입니다.

(지금까지 게시된 솔루션에 이 접근 방식이 없다는 것이 이 답변을 작성하는 주된 이유입니다.)

 ${var#*SubStr} # drops substring from start of string up to first occurrence of `SubStr` ${var##*SubStr} # drops substring from start of string up to last occurrence of `SubStr` ${var%SubStr*} # drops substring from last occurrence of `SubStr` to end of string ${var%%SubStr*} # drops substring from first occurrence of `SubStr` to end of string

Score_Under에서 설명한 대로:

#% 는 각각 문자열 의 시작 에서 가장 짧은 일치하는 하위 문자열을 삭제하고,

##%% 는 일치하는 가능한 가장 긴 부분 문자열을 삭제합니다.

위의 구문을 사용하여 구분 기호 이하의 하위 문자열을 삭제하여 문자열에서 하위 문자열 "요소"를 추출하는 접근 방식을 만들 수 있습니다.

아래 코드 블록은 (Mac OS의 bash ), , 에서 잘 작동합니다.

(Adam Katz의견 덕분에 이 루프가 훨씬 더 간단해졌습니다!)

 IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>" while [ "$IN" != "$iter" ] ;do # extract the substring from start of string up to delimiter. iter=${IN%%;*} # delete this first "element" AND next separator, from $IN. IN="${IN#$iter;}" # Print (or doing anything with) the first "element". echo "> [$iter]" done # > [bla@some.com] # > [john@home.com] # > [Full Name <fulnam@other.org>]

즐거운 시간 보내세요!


F. Hauri

cut 명령을 참조하는 몇 가지 답변을 보았지만 모두 삭제되었습니다. 아무도 그것에 대해 자세히 설명하지 않은 것이 조금 이상합니다. 왜냐하면 이 명령이 특히 구분된 로그 파일을 구문 분석할 때 이러한 유형의 작업을 수행하는 데 더 유용한 명령 중 하나라고 생각하기 때문입니다.

이 특정 예제를 bash 스크립트 배열로 분할하는 경우 tr 이 더 효율적일 cut 사용할 수 있으며 중간에서 특정 필드를 가져오려는 경우 더 효과적입니다.

예시:

 $ echo "bla@some.com;john@home.com" | cut -d ";" -f 1 bla@some.com $ echo "bla@some.com;john@home.com" | cut -d ";" -f 2 john@home.com

분명히 루프에 넣고 -f 매개 변수를 반복하여 각 필드를 독립적으로 가져올 수 있습니다.

다음과 같은 행이 있는 구분된 로그 파일이 있을 때 더 유용합니다.

 2015-04-27|12345|some action|an attribute|meta data

cut 할 수있는 매우 편리 cat 이 파일을 추가 처리를 위해 특정 필드를 선택합니다.


DougW

이것은 나를 위해 일했습니다.

 string="1;2" echo $string | cut -d';' -f1 # output is 1 echo $string | cut -d';' -f2 # output is 2

Steven Lizarazo

AWK 가 문제를 해결하는 가장 효율적이고 효과적인 명령이라고 생각합니다. AWK는 거의 모든 Linux 배포판에 기본적으로 포함되어 있습니다.

 echo "bla@some.com;john@home.com" | awk -F';' '{print $1,$2}'

줄게

 bla@some.com john@home.com

물론 awk 인쇄 필드를 재정의하여 각 이메일 주소를 저장할 수 있습니다.


Tong

이 접근 방식은 어떻습니까?

 IN="bla@some.com;john@home.com" set -- "$IN" IFS=";"; declare -a Array=($*) echo "${Array[@]}" echo "${Array[0]}" echo "${Array[1]}"

원천


errator

echo "bla@some.com;john@home.com" | sed -e 's/;/\n/g' bla@some.com john@home.com

lothar

이것은 또한 작동합니다:

 IN="bla@some.com;john@home.com" echo ADD1=`echo $IN | cut -d \; -f 1` echo ADD2=`echo $IN | cut -d \; -f 2`

주의하십시오. 이 솔루션이 항상 올바른 것은 아닙니다. "bla@some.com"만 전달하면 ADD1과 ADD2에 모두 할당됩니다.


Ashok

Darron의 답변 에 대한 다른 견해는 다음과 같습니다.

 IN="bla@some.com;john@home.com" read ADDR1 ADDR2 <<<$(IFS=";"; echo $IN)

nickjb

배열을 사용하지 않는 경우 이 하나의 라이너는 어떻습니까?

 IFS=';' read ADDR1 ADDR2 <<<$IN

Darron

방탄 방식인 Bash에서는 변수에 개행 문자가 포함되어 있어도 작동합니다.

 IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")

바라보다:

 $ in=$'one;two three;*;there is\na newline\nin this field' $ IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in") $ declare -p array declare -a array='([0]="one" [1]="two three" [2]="*" [3]="there is a newline in this field")'

이것이 작동하는 비결 read -d 옵션을 빈 구분 기호와 함께 사용하여 read 가 공급되는 모든 것을 읽도록 강제하는 것입니다. 그리고 우리는 피드 read 변수의 정확히 컨텐츠 in 아무런 뒤에 줄 바꿈 덕분에, printf . read 전달된 문자열에 후행 구분 기호가 있는지 확인하기 위해 printf 에 구분 기호도 넣었습니다. 그것이 없으면 read 는 잠재적인 후행 빈 필드를 트리밍합니다.

 $ in='one;two;three;' # there's an empty field $ IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in") $ declare -p array declare -a array='([0]="one" [1]="two" [2]="three" [3]="")'

후행 빈 필드는 유지됩니다.


Bash≥4.4 업데이트

Bash 4.4부터 내장 mapfile (일명 readarray )은 구분 기호를 지정하는 -d 따라서 또 다른 정식 방법은 다음과 같습니다.

 mapfile -d ';' -t array < <(printf '%s;' "$in")

gniourf_gniourf

IFS를 설정하지 않고

콜론이 하나만 있으면 다음과 같이 할 수 있습니다.

 a="foo:bar" b=${a%:*} c=${a##*:}

당신은 얻을 것이다:

 b = foo c = bar

Emilien Brigand

다음은 깨끗한 3-라이너입니다.

 in="foo@bar;bizz@buzz;fizz@buzz;buzz@woof" IFS=';' list=($in) for item in "${list[@]}"; do echo $item; done

여기서 IFS 구분 기호를 기반으로 단어를 구분하고 () 는 배열 을 만드는 데 사용됩니다. 그런 다음 [@] 를 사용하여 각 항목을 별도의 단어로 반환합니다.

그 이후에 코드가 있으면 $IFS 도 복원해야 합니다(예: unset IFS .


kenorb

다음 Bash/zsh 함수는 첫 번째 인수를 두 번째 인수로 지정된 구분 기호로 분할합니다.

 split() { local string="$1" local delimiter="$2" if [ -n "$string" ]; then local part while read -d "$delimiter" part; do echo $part done <<< "$string" echo $part fi }

예를 들어, 명령

 $ split 'a;b;c' ';'

수익률

 a b c

예를 들어 이 출력은 다른 명령으로 파이프될 수 있습니다. 예시:

 $ split 'a;b;c' ';' | cat -n 1 a 2 b 3 c

제공된 다른 솔루션과 비교할 때 이 솔루션은 다음과 같은 장점이 있습니다.

  • IFS 는 재정의되지 않습니다. 로컬 변수의 동적 범위 지정으로 인해 IFS 를 재정의하면 새 값이 루프 내에서 수행되는 함수 호출로 누출됩니다.

  • 배열은 사용되지 않습니다. 읽기를 사용하여 문자열을 배열로 read 려면 Bash에서 -a 플래그가 필요하고 zsh에서 -A

원하는 경우 함수를 다음과 같이 스크립트에 넣을 수 있습니다.

 #!/usr/bin/env bash split() { # ... } split "$@"

Halle Knast

많은 상황에 awk를 적용할 수 있습니다.

 echo "bla@some.com;john@home.com"|awk -F';' '{printf "%s\n%s\n", $1, $2}'

또한 당신은 이것을 사용할 수 있습니다

 echo "bla@some.com;john@home.com"|awk -F';' '{print $1,$2}' OFS="\n"

shuaihanhungry

다음과 같은 간단하고 현명한 방법이 있습니다.

 echo "add:sfff" | xargs -d: -i echo {}

그러나 gnu xargs를 사용해야 하며, BSD xargs는 -d delim을 지원할 수 없습니다. 저처럼 애플맥을 사용하신다면 gnu xargs를 설치할 수 있습니다.

 brew install findutils

그 다음에

 echo "add:sfff" | gxargs -d: -i echo {}

Victor Choy

가장 간단한 방법입니다.

 spo='one;two;three' OIFS=$IFS IFS=';' spo_array=($spo) IFS=$OIFS echo ${spo_array[*]}

Heavy Gray

공간이 없다면 왜 안되나요?

 IN="bla@some.com;john@home.com" arr=(`echo $IN | tr ';' ' '`) echo ${arr[0]} echo ${arr[1]}

ghost

여기에 몇 가지 멋진 답변이 있습니다(errator esp.). 그러나 다른 언어로 나누는 것과 유사한 것에 대해 - 이것이 내가 원래 질문을 의미하는 것으로 간주한 것입니다 - 나는 이것에 정착했습니다.

 IN="bla@some.com;john@home.com" declare -aa="(${IN/;/ })";

이제 ${a[0]} , ${a[1]} 등은 예상대로입니다. ${#a[*]} 를 사용합니다. 또는 물론 반복하려면 다음을 수행하십시오.

 for i in ${a[*]}; do echo $i; done

중요 사항:

이것은 걱정할 공간이 없는 경우에 작동하여 내 문제는 해결되었지만 당신의 문제는 해결되지 않을 수 있습니다. 이 경우 $IFS 솔루션을 사용하십시오.


eukras

IN="bla@some.com;john@home.com" IFS=';' read -a IN_arr <<< "${IN}" for entry in "${IN_arr[@]}" do echo $entry done

산출

 bla@some.com john@home.com

시스템 : 우분투 12.04.1


rashok

set 사용하여 $@ 배열을 로드합니다.

 IN="bla@some.com;john@home.com" IFS=';'; set $IN; IFS=$' \t\n'

그런 다음 파티를 시작합니다.

 echo $# for a; do echo $a; done ADDR1=$1 ADDR2=$2

jeberle

bash 배열이 필요하지 않은 두 가지 bourne-ish 대안:

사례 1 : 멋지고 간단하게 유지하십시오. NewLine을 레코드 구분 기호로 사용하십시오. 예를 들어.

 IN="bla@some.com john@home.com" while read i; do # process "$i" ... eg. echo "[email:$i]" done <<< "$IN"

참고: 이 첫 번째 경우에는 목록 조작을 지원하기 위해 하위 프로세스가 분기되지 않습니다.

아이디어: NL을 내부적으로 광범위하게 사용하고 외부 에서 최종 결과를 생성할 때만 다른 RS로 변환하는 것이 가치가 있을 수 있습니다.

사례 2 : ";" 레코드 구분자로... 예.

 NL=" " IRS=";" ORS=";" conv_IRS() { exec tr "$1" "$NL" } conv_ORS() { exec tr "$NL" "$1" } IN="bla@some.com;john@home.com" IN="$(conv_IRS ";" <<< "$IN")" while read i; do # process "$i" ... eg. echo -n "[email:$i]$ORS" done <<< "$IN"

두 경우 모두 루프 내에서 하위 목록을 구성할 수 있으며 루프가 완료된 후에도 지속됩니다. 이것은 목록을 파일에 저장하는 대신 메모리에서 목록을 조작할 때 유용합니다. {ps 침착하고 B-) }


NevilleDNZ

이미 제공된 환상적인 답변 외에도 데이터를 인쇄하는 문제라면 awk 사용을 고려할 수 있습니다.

 awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN"

이렇게 하면 필드 구분 기호가 로 설정됩니다 ; for 루프로 필드를 반복하고 그에 따라 인쇄할 수 있습니다.

시험

 $ IN="bla@some.com;john@home.com" $ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN" > [bla@some.com] > [john@home.com]

다른 입력:

 $ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "a;b;cd;e_;f" > [a] > [b] > [cd] > [e_] > [f]

fedorqui 'SO stop harming'

Android 셸에서는 대부분의 제안된 방법이 작동하지 않습니다.

 $ IFS=':' read -ra ADDR <<<"$PATH" /system/bin/sh: can't create temporary file /sqlite_stmt_journals/mksh.EbNoR10629: No such file or directory

작동하는 것은 다음과 같습니다.

 $ for i in ${PATH//:/ }; do echo $i; done /sbin /vendor/bin /system/sbin /system/bin /system/xbin

여기서 // 는 전역 대체를 의미합니다.


18446744073709551615

IN='bla@some.com;john@home.com;Charlie Brown <cbrown@acme.com;!"#$%&/()[]{}*? are no problem;simple is beautiful :-)' set -f oldifs="$IFS" IFS=';'; arrayIN=($IN) IFS="$oldifs" for i in "${arrayIN[@]}"; do echo "$i" done set +f

산출:

 bla@some.com john@home.com Charlie Brown <cbrown@acme.com !"#$%&/()[]{}*? are no problem simple is beautiful :-)

설명: 괄호()를 사용한 단순 할당은 수행하는 동안 올바른 IFS가 있는 경우 세미콜론으로 구분된 목록을 배열로 변환합니다. 표준 FOR 루프는 평소와 같이 해당 배열의 개별 항목을 처리합니다. IN 변수에 대해 주어진 목록은 "하드" 따옴표로 묶어야 합니다. 즉, 단일 틱으로 표시해야 합니다.

Bash는 할당을 명령과 같은 방식으로 처리하지 않으므로 IFS를 저장하고 복원해야 합니다. 다른 해결 방법은 함수 내부에 할당을 래핑하고 수정된 IFS를 사용하여 해당 함수를 호출하는 것입니다. 이 경우 별도의 IFS 저장/복원이 필요하지 않습니다. 지적해주신 "Bize"님께 감사드립니다.


ajaaskel

좋아 얘들아!

여기 내 대답이 있습니다!

 DELIMITER_VAL='=' read -d '' F_ABOUT_DISTRO_R <<"EOF" DISTRIB_ID=Ubuntu DISTRIB_RELEASE=14.04 DISTRIB_CODENAME=trusty DISTRIB_DESCRIPTION="Ubuntu 14.04.4 LTS" NAME="Ubuntu" VERSION="14.04.4 LTS, Trusty Tahr" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 14.04.4 LTS" VERSION_ID="14.04" HOME_URL="http://www.ubuntu.com/" SUPPORT_URL="http://help.ubuntu.com/" BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" EOF SPLIT_NOW=$(awk -F$DELIMITER_VAL '{for(i=1;i<=NF;i++){printf "%s\n", $i}}' <<<"${F_ABOUT_DISTRO_R}") while read -r line; do SPLIT+=("$line") done <<< "$SPLIT_NOW" for i in "${SPLIT[@]}"; do echo "$i" done

이 접근 방식이 나에게 "최고"인 이유는 무엇입니까?

두 가지 이유 때문에:

  1. 구분 기호 를 이스케이프할 필요 가 없습니다.
  2. 당신은 공백에 문제 가 없을 것입니다. 값은 배열에서 적절하게 분리됩니다!

[]'NS


Eduardo Lucio

';'으로 구분된 문자열을 분할하는 한 줄짜리 배열로 다음과 같습니다.

 IN="bla@some.com;john@home.com" ADDRS=( $(IFS=";" echo "$IN") ) echo ${ADDRS[0]} echo ${ADDRS[1]}

이것은 서브쉘에 IFS만 설정하므로 값을 저장하고 복원하는 것에 대해 걱정할 필요가 없습니다.


Michael Hale

내 솔루션: perlsplit 을 사용하여 작업을 수행하는 기능.

자세한 설명:

 #!/bin/bash # This function is a wrapper for Perl's split.\ # \ # Since we cannot return an array like in Perl, # it takes the name of the resulting array as last # argument.\ # \ # See https://perldoc.perl.org/functions/split for usage info # and examples.\ # \ # If you provide a Perl regexp that contains eg an escaped token like \b, # space(s) and/or capture group(s), it must be quoted, and eg /\b/ must # be single-quoted.\ # Thus, it's best to generally single-quote a Perl regexp. function split # Args: <Element separator regexp> <string> <array name> { (($# != 3)) && echo "${FUNCNAME[0]}: Wrong number of arguments, returning." && return 1 local elementSepRE=$1 local string=$2 local -n array=$3 local element i=0 # Attention! read does Word Splitting on each line! # I must admit I didn't know that so far. # This removes leading and trailing spaces, exactly # what we don't want. # Thus, we set IFS locally to newline only. local IFS=$'\n' while read element; do # As opposed to array+=($element), # this preserves leading and trailing spaces. array[i++]=$element done <<<$(_perl_split) } # This function calls Perl's split function and prints the elements of the # resulting array on separate lines.\ # It uses the caller's $elementSepRE and $string. function _perl_split { # A heredoc is a great way of embedding a Perl script. # NB: - Shell variables get expanded. # - Thus: # - They must be quoted. # - Perl scalar variables must be escaped. # - The backslash of \n must be escaped to protect it. # - Instead of redirecting a single heredoc to perl, we may # use multiple heredocs with cat within a command group and # pipe the result to perl. # This enables us to conditionally add certain lines of code. { cat <<-END my \$elementSepRE=q($elementSepRE); END # If $elementSepRE is a literal Perl regexp, qr must be applied # to it in order to use it. # NB: We cannot write this condition in Perl because when perl # compiles the script, all statements are checked for validity, # no matter if they will actually be executed or not. # And if $elementSepRE was eg == ', the line below – although # not to be executed – would give an error because of an unterminated # single-quoted string. [[ $elementSepRE =~ ^m?/ && $elementSepRE =~ /[msixpodualn]*$ ]] && cat <<-END \$elementSepRE=qr$elementSepRE; END cat <<-END my @array=split(\$elementSepRE, q($string)); print(\$_ . "\\n") for (@array); END } | perl }

그리고 무슨 일이 일어나고 있는지 한 눈에 보는 사람들을 위해 코멘트없이 동일합니다 ;)

 #!/bin/bash # This function is a wrapper for Perl's split.\ # \ # Since we cannot return an array like in Perl, # it takes the name of the resulting array as last # argument.\ # \ # See https://perldoc.perl.org/functions/split for usage info # and examples.\ # \ # If you provide a Perl regexp that contains eg an escaped token like \b, # space(s) and/or capture group(s), it must be quoted, and eg /\b/ must # be single-quoted.\ # Thus, it's best to generally single-quote a Perl regexp. function split # Args: <Element separator regexp> <string> <array name> { (($# != 3)) && echo "${FUNCNAME[0]}: Wrong number of arguments, returning." && return 1 local elementSepRE=$1 local string=$2 local -n array=$3 local element i=0 local IFS=$'\n' while read element; do array[i++]=$element done <<<$(_perl_split) } function _perl_split { { cat <<-END my \$elementSepRE=q($elementSepRE); END [[ $elementSepRE =~ ^m?/ && $elementSepRE =~ /[msixpodualn]*$ ]] && cat <<-END \$elementSepRE=qr$elementSepRE; END cat <<-END my @array=split(\$elementSepRE, q($string)); print(\$_ . "\\n") for (@array); END } | perl }

Christoph

출처 : http:www.stackoverflow.com/questions/918886/how-do-i-split-a-string-on-a-delimiter-in-bash

반응형