일치하는 {}에서 쉼표를 무시하고 쉼표로 구분된 목록을 분할합니다.

일치하는 {}에서 쉼표를 무시하고 쉼표로 구분된 목록을 분할합니다.

CSV를 분할하고 싶지만 중괄호 그룹 내의 쉼표 일치를 무시하고 각 목록 구성원을 반복합니다. 아래 코드는 훌륭하게 작동하지만 중괄호 그룹 내의 쉼표를 고려하지 않습니다.

가정:

  • 여기 있을 것이다언제나일치하는 버팀대 쌍입니다. 즉, {{ {a,b,c}, xwill 과 같은 입력이아니요발생하다.

예상 출력:

Word='{0,1}'
Word='alpha'
Word='{(x,y,z)}'
Word='{{1,2,3}, {a,b,c}}'

인용하다:

암호:

#!/bin/bash

#TEST_STRING="alpha, beta, gamma" ## <--- works great for simple case
TEST_STRING="{0,1}, alpha, {(x,y,z)}, {{1,2,3}, {a,b,c}}"

echo "${TEST_STRING}" | sed -n 1'p' | tr ',' '\n' | while read Extracted_Word; do
    printf "Word='%s'\n" "${Extracted_Word}"
done

나는 123의 (현재 삭제된) 솔루션을 적용해 보았습니다.

#!/bin/bash

#TEST_STRING="alpha, beta, gamma" ## <--- works great for simple case
TEST_STRING="{0,1}, alpha, {(x,y,z)}, {{1,2,3}, {a,b,c}}"

echo "${TEST_STRING}" \
    | sed -n 1'p' \
    | sed 's/\({[^}]*\({[^}]*}[^}]*\)*} *\)\(,\|$\) */\1\n/g;:1;s/\(\n[^{}]*\), */\1\n/;t1' \
    | tr ',' '\n' \
    | while read Extracted_Word; do
    printf "Word='%s'\n" "${Extracted_Word}"
done

하지만 이로 인해 다음과 같은 오류 메시지가 생성됩니다.

./testcsv.sh
sed: 1: "s/\({[^}]*\({[^}]*}[^}] ...": bad flag in substitute command: ':'
./testcsv.sh: line 18: {{ {a,b,c}, x: command not found

답변1

순수한 시도세게 때리다

#!/bin/bash
TEST_STRING="{0,1}, alpha, {(x,y,z)}, {{1,2,3}, {a,b,c}}"
TEST_STRING="$TEST_STRING"","
count=0
newword=''
while [ "${TEST_STRING::1}" ] ; do 
    l="${TEST_STRING::1}"
    TEST_STRING=${TEST_STRING:1}
    [ "$l" = '{' ] && ((count++))
    [ "$l" = '}' ] && ((count--))
    if [ "$l" = ',' ] && ! ((count)) ; then
        echo "Word='$newword'"
        newword=''
    else
        if [ "$newword" ] || [ "$l" != " " ] ; then
            newword="$newword""$l"
        fi
    fi
done

답변2

다음은 예제를 분할할 sed 스크립트입니다.

#!/bin/sed -Ef

# replace all commas with newlines
s/,/\
/g

# Do we need to re-join any lines?
:loop
# Unmatched brace containing possibly another (matched) level of
# braces:
s/(\{([^{}]|\{[^{}]*\})*)\
/\1,/
tloop

# remove any leading space
s/\n */\
/g

# At first line, print result, then exit.
1q

경고: 질문의 설명에 따라 두 가지 수준의 중괄호만 처리합니다.

시험:

$ ./259252.sed <<<'{0,1}, alpha, {(x,y,z)}, {{1,2,3}, {a,b,c}}'
{0,1}
alpha
{(x,y,z)}
{{1,2,3}, {a,b,c}}

첫 번째 줄을 처리한 후 종료되는 것을 보여줍니다.

$ ./259252.sed <<<$'a,b,c\nd,e,f'
a
b
c

나는 이것을 Linux에서 실행하고 다음 답변을 사용하고 있습니다.Mac OSX의 sed와 다른 "표준" sed의 차이점은 무엇입니까?MacOS로 포팅하세요. 이것이 작동하지 않으면이 답변brew install gnu-sedsed를 사용하여 GNU를 설치 한 후 호출하는 gsed대신 사용하는 것이 좋습니다 .sed

사용 중:

#!/bin/bash

TEST_STRING="{0,1}, alpha, {(x,y,z)}, {{1,2,3}, {a,b,c}}"

echo "${TEST_STRING}" | sed -E -f 259252.sed | while read Extracted_Word; do
    printf "Word='%s'\n" "${Extracted_Word}"
done

이것은 만든다:

Word='{0,1}'
Word='alpha'
Word='{(x,y,z)}'
Word='{{1,2,3}, {a,b,c}}'

답변3

str='{0,1},alpha,{(x,y,z)},{{1,2,3},{a,b,c}}'
OPTIND=1 l=0 r=0; set ""
while   getopts : na -"$str"
do      [ "$l" -gt "$r" ]
        case    $?$OPTARG  in
        (1,)  ! l=0 r=0    ;;
        (0})    r=$((r+1)) ;;
        (?{)    l=$((l+1)) ;;
        esac    &&
        set -- "$@$OPTARG" ||
        set -- "$@" ""
done;   printf  %s\\n "$@"

dash버그가 있으며 다음과 같은 것이 필요합니다.

set -- "$@" ""; str=${str#?}

...하지만 그 외에는 위의 작업이 매우 빠르고 기본적으로 모든 POSIX 셸에서 작동하며 매우 간단해야 합니다. 일치하지 않는 쌍도 처리해야 합니다.(꼭 그럴 필요가 없더라도)}선행 항목 앞에 나타나는 a 를 무시하여 특별히 인식합니다 {.


{0,1}
alpha
{(x,y,z)}
{{1,2,3},{a,b,c}}

접두사 문자열과 주변 따옴표를 얻으려면 다음을 바꿀 수 있습니다.

printf "Word='%s'\n" "$@"

... printf %s\\n "$@"위에 사용된 것입니다. 여기에 예제 값이 주어지면 $str다음이 인쇄됩니다.

Word='{0,1}'
Word='alpha'
Word='{(x,y,z)}'
Word='{{1,2,3},{a,b,c}}'

당신은 더 결심했을 수도 있습니다 ...

for W do alias "Word=$W" Word; done

...결과는...

Word='{0,1}'
Word=alpha
Word='{(x,y,z)}'
Word='{{1,2,3},{a,b,c}}'

...필요에 따라 인용하고 포함된 큰 따옴표도 올바르게 인용합니다.( 그러나 를 사용하는 경우 bash먼저 이 작업을 수행해야 할 수도 있습니다 set --posix).

그래서 시연을 하자면...

str="{0,1

}}, {,}alph}'a, {(x,y,z)}, {{1,2,3}, {a,b,c}}" 
OPTIND=1 l=0 r=0; set ""
while   getopts : na -"$str"
do      [ "$l" -gt "$r" ]
        case    $?$OPTARG  in
        (1,)  ! l=0 r=0    ;;
        (0})    r=$((r+1)) ;;
        (?{)    l=$((l+1)) ;;
        esac    &&
        set -- "$@$OPTARG" ||
        set -- "$@" ""
done;   for W do alias "Word=${W# }" Word
done

Word='{0,1

}}'
Word='{,}alph}'\''a'
Word='{(x,y,z)}'
Word='{{1,2,3}, {a,b,c}}'

...선행 공백 처리도 매우 간단합니다...

답변4

또 다른 bash 솔루션:

  • 일치하지 않는 중괄호 쌍을 처리합니다 {.
  • 하나 이상의 여는 중괄호가 나타날 때까지는 닫는 중괄호가 허용되지 않습니다.
  • 줄 끝에서 중괄호 개수를 0으로 재설정합니다.
  • 여는 중괄호보다 닫는 중괄호가 더 많은 경우 쉼표는 유효한 쉼표로 허용됩니다.
  • 솔루션 앞의 공백이 제거됩니다.
  • 결과 단어가 인용됩니다.

암호:

str="}}{0,1}}, {,}alph}'a"

            fin='false' d='0'
until  $fin
do     IFS=   read -r -d '' -n 1 a || fin='true'
       if     [[ $a == '{' ]] ; then (( d++ )) ; fi ### count openning braces.
       if     [[ $a == ',' ]] && (( d<1 )) || $fin  ### ',' out of braces or end.
       then   $fin && s="${s%$'\n'}"                ### removing a last newline.
              set -- "$@" "$s"                      ### store in an array.
              unset a s d                           ### unset working variables.
       fi
       if [[ $a == '}' ]] && ((d>0)); then ((d--)); fi  ### close braces.
       s="$s$a"
done <<<"$str"
printf 'Word=%q\n' "${@# }"       ### print a quoted value removing front space.

산출:

Word=\}\}\{0\,1\}\}
Word=\{\,\}alph\}\'a

아니면 좀 더 신비한 것:

str="{0,1

}}, {,}alph}'a, {(x,y,z)}, {{1,2,3}, {a,b,c}}"

        fin='false' d='0'
until  $fin
do     IFS=   read -r -d '' -n 1 a || fin='true'
       [[ $a == '{' ]] && (( d++ ))                 ### count openning braces.
       [[ $a == ',' ]] && (( d<1 )) || $fin && {    ### ',' no braces (or end).
              $fin && s="${s%$'\n'}"                ### removing a last newline.
              set -- "$@" "$s"                      ### store in an array.
              unset a s d                           ### unset working variables.
       }
       [[ $a == '}' ]] && (( d>0 )) && ((d--))      ### substract closing braces.
       s="$s$a"
done <<<"$str"
printf 'Word=%q\n' "${@# }"    ### print a quoted value with front space removed.

결과:

Word=$'{0,1\n\n}}'
Word=\{\,\}alph\}\'a
Word=\{\(x\,y\,z\)\}
Word=\{\{1\,2\,3\}\,\ \{a\,b\,c\}\}

관련 정보