拆分逗号分隔列表,忽略匹配的 {} 中的逗号

拆分逗号分隔列表,忽略匹配的 {} 中的逗号

我想拆分 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。如果这不起作用,那么这个答案建议您可以使用 安装 GNU sed brew install gnu-sed,然后使用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 shell 中工作,而且非常简单。它还应该处理不匹配的对(即使你不需要)通过忽略特别识别}出现在前导 之前的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}}'

...根据需要引用,并且也会正确引用嵌入的硬引号(不过,如果使用 a 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\}\}

相关内容