IFS를 사용하여 문자열 분할

IFS를 사용하여 문자열 분할

문자열을 분할하는 샘플 스크립트를 작성했지만 예상대로 작동하지 않습니다.

#!/bin/bash
IN="One-XX-X-17.0.0"
IFS='-' read -r -a ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
    echo "Element:$i"
done
#split 17.0.0 into NUM
IFS='.' read -a array <<<${ADDR[3]};
for element in "${array[@]}"
do
    echo "Num:$element"
done
  • 실제 출력
    One
    XX
    X
    17.0.0
    17 0 0
    
  • 하지만 내 예상 결과는 다음과 같습니다.
    One
    XX
    X
    17.0.0
    17
    0
    0
    

답변1

이전 버전에서는 bash나중에 변수를 인용해야 했습니다 <<<. 이 문제는 4.4에서 수정되었습니다. 이전 버전에서는 변수가 IFS에서 분할되고 <<<결과 단어가 리디렉션을 구성하는 임시 파일에 저장되기 전에 공간적으로 연결되었습니다.

4.2 및 이전 버전에서는 read또는 같은 내장 함수를 리디렉션할 때 command분할이 해당 내장 함수의 IFS도 사용했습니다(4.3에서는 이 문제가 수정되었습니다).

$ bash-4.2 -c 'a=a.b.c.d; IFS=. read x <<< $a; echo  "$x"'
a b c d
$ bash-4.2 -c 'a=a.b.c.d; IFS=. cat <<< $a'
a.b.c.d
$ bash-4.2 -c 'a=a.b.c.d; IFS=. command cat <<< $a'
a b c d

이 문제는 4.3에서 해결되었습니다.

$ bash-4.3 -c 'a=a.b.c.d; IFS=. read x <<< $a; echo  "$x"'
a.b.c.d

하지만 $a여전히 단어 분할의 영향을 받습니다.

$ bash-4.3 -c 'a=a.b.c.d; IFS=.; read x <<< $a; echo  "$x"'
a b c d

4.4에서:

$ bash-4.4 -c 'a=a.b.c.d; IFS=.; read x <<< $a; echo  "$x"'
a.b.c.d

이전 버전으로 포팅하려면 변수를 인용하세요(또는 zsh변수 소스를 먼저 사용하면 해당 문제가 존재하지 않습니다).<<<

$ bash-any-version -c 'a=a.b.c.d; IFS=.; read x <<< "$a"; echo "$x"'
a.b.c.d

문자열을 분할하는 이 방법은 개행 문자가 포함되지 않은 문자열에 대해서만 작동합니다. 또한 , , , a..b.c.로 분할됩니다 (마지막 요소는 비어 있지 않음)."a""""b""c"

임의의 문자열을 분할하려면 분할+glob 연산자를 사용할 수 있습니다(이렇게 하면 표준이 되고 이전과 같이 변수 내용을 임시 파일에 저장하지 않아도 됩니다 <<<).

var='a.new
line..b.c.'
set -o noglob # disable glob
IFS=.
set -- $var'' # split+glob
for i do
  printf 'item: <%s>\n' "$i"
done

또는:

array=($var'') # in shells with array support

이는 ''후행 빈 요소(있는 경우)를 보존하기 위한 것입니다. 또한 빈 요소를 $var빈 요소로 분할합니다.

또는 적절한 분할 연산자와 함께 셸을 사용하세요.

  • zsh:

    array=(${(s:.:)var} # removes empty elements
    array=("${(@s:.:)var}") # preserves empty elements
    
  • rc:

    array = ``(.){printf %s $var} # removes empty elements
    
  • fish

    set array (string split . -- $var) # not for multiline $var
    

답변2

수리(또한 참조S.Chazeras의 답변배경용), 합리적인 출력:

#!/bin/bash
IN="One-XX-X-17.0.0"
IFS='-' read -r -a ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
    if [ "$i" = "${i//.}" ] ; then 
        echo "Element:$i" 
        continue
    fi
    # split 17.0.0 into NUM
    IFS='.' read -a array <<< "$i"
    for element in "${array[@]}" ; do
        echo "Num:$element"
    done
done

산출:

Element:One
Element:XX
Element:X
Num:17
Num:0
Num:0

노트:

  • 조건을 추가하는 것이 좋습니다2위반지존재하다이것처음반지 모양.

  • bash패턴 교체()는 "${i//.}"요소에 이 있는지 확인합니다. .( case문은 다음과 유사하지만 더 간단할 수 있습니다.OP암호. )

  • read$array입력을 통한 ing은 <<< "${ADDR[3]}"ing만큼 다재다능하지 않습니다 <<< "$i". 알 필요가 없어진다어느요소에는 .s가 있습니다.

  • 이 코드는 "요소: 17.0.0"의도치 않은 일이었어. 만약 그런 행동이의도적으로 메인 루프를 다음으로 교체합니다.

    for i in "${ADDR[@]}"; do
       echo "Element:$i" 
       if [ "$i" != "${i//.}" ] ; then 
       # split 17.0.0 into NUM
           IFS='.' read -a array <<< "$i"
           for element in "${array[@]}" ; do
               echo "Num:$element"
           done
       fi
    done
    

답변3

그리고한 줄의 비용이 발생합니다.

IN="One-XX-X-17.0.0"

awk -F'[-.]' '{ for(i=1;i<=NF;i++) printf "%s : %s\n",($i~/^[0-9]+$/?"Num":"Element"),$i }' <<<"$IN"
  • -F'[-.]'-- 우리의 경우에는 여러 문자를 기반으로 하는 필드 구분 기호입니다..

산출:

Element : One
Element : XX
Element : X
Num : 17
Num : 0
Num : 0

답변4

이것이 내가 하는 방법이다:

OIFS=$IFS
IFS='-'
IN="One-XX-X-17.0.0"
ADDR=($IN)
for i in "${ADDR[@]}"; do
 echo "Element:$i"
done
IFS='.'
array=(${ADDR[3]})
for element in "${array[@]}"
do
  echo "Num:$element"
done

결과는 예상대로입니다.

Num:17
Num:0
Num:0

관련 정보