String Bash将数据集拆分为每对1行

String Bash将数据集拆分为每对1行,string,bash,loops,String,Bash,Loops,首先,如果标题不是最清楚的话,我很抱歉,我真的不知道如何更好地表达这个问题 基本上,我将数据接收到bash脚本中(我无法控制所述数据的格式),该脚本以以下格式到达: (Name: Foo bar; UUID: <blah-blah-0101>; AnotherField: Some text; TieredField: (Number: 123; Text: More Text; YetAnotherTier: (Name: somename; IP: 125.214.21.4) ; )

首先,如果标题不是最清楚的话,我很抱歉,我真的不知道如何更好地表达这个问题

基本上,我将数据接收到bash脚本中(我无法控制所述数据的格式),该脚本以以下格式到达:

(Name: Foo bar; UUID: <blah-blah-0101>; AnotherField: Some text; TieredField: (Number: 123; Text: More Text; YetAnotherTier: (Name: somename; IP: 125.214.21.4) ; ) ; NumericalData: 4; MoreInfo: Some Information) ; (名称:Foo-bar;UUID:;另一个字段:某些文本;TieredField:(编号:123;文本:更多文本;YetAnotherTier:(名称:somename;IP:125.214.21.4););数字数据:4;更多信息:一些信息); 现在我要做的是循环遍历每个键/值对,以便处理信息。显然,删除前导/尾随“();”很简单。然后我想可能会用换行符替换“;”,但由于层次不同,这会中断

关于层次,我不关心它们内部的循环,我只关心最高层次。因此:

TieredField: (Number: 123; Text: More Text; YetAnotherTier: (Name: somename; IP: 125.214.21.4) ; ) TieredField:(编号:123;文本:更多文本;YetAnotherTier:(名称:somename;IP:125.214.21.4);) 就我而言,这是一双简单的鞋

预期成果:

Name: Foo bar UUID: AnotherField: Some text TieredField: (Number: 123; Text: More Text; YetAnotherTier: (Name: somename; IP: 125.214.21.4) ; ) NumericalData: 4 MoreInfo: Some Information 名称:富吧 UUID: 另一个字段:一些文本 TieredField:(编号:123;文本:更多文本;YetAnotherTier:(名称:somename;IP:125.214.21.4);) 数字数据:4 更多信息:一些信息 因为我熟悉文本块的行循环,所以将原始字符串转换为上述结果就足够了,尽管直接循环上述每一行的答案也可以

我真的不知道如何实现这一点,所以任何方向都是值得赞赏的。

它的工作原理是:

# strip stdin up until first '(' is read
cut -d '(' -f2- | while read -r -n1 c; do
        case $c in
        ')') break; ;;
        # if read any char, this is field name, just print it
        [a-zA-Z]) echo -n "$c"; ;;
        # doublescore separates names from values
        :)
                echo -n ': '
                l=0
                while read -n1 c; do
                        case "$c" in
                        # we need to count levels of '(' ')'
                        '(') ((l++)); echo -n '('; ;;
                        ')') ((l--)); 
                             # if level gets under zero, break from here, look at `MoreInfo:` case
                             if ((l<0)); then 
                                 echo; break; 
                             else 
                                 echo -n ')'; 
                                 if ((l==0)); then 
                                     echo; break; 
                                 fi;
                             fi;
                             ;;
                        # ';' separetes the next field, but only if level is zero, cause otherwise those are nested fields
                        ';') 
                                if ((l==0)); then 
                                        echo; 
                                        break;
                                else 
                                        echo -n "$c"; 
                                fi;
                                ;;
                        *) echo -n "$c"; ;;
                        esac
                done;
                # if level is lower then zero, braek, look at `MoreInfo:` case
                if ((l<0)); then break; fi;
                ;;
        " ") ;;
        esac
done; 
cat >/dev/null
#将stdin向上剥离,直到读取到第一个“(”
当读取-r-n1 c时切-d'('-f2-|);do
每箱$c
)打破;;
#如果读取任何字符,这是字段名,只需打印即可
[a-zA-Z])echo-n“$c”;;
#doublescore将名称与值分开
:)
回声-n':'
l=0
而读取-n1c;做
大写“$c”
#我们需要计算'('')的级别
“(”)((l++);echo-n'(';;
""(l--),;
#如果级别低于零,从这里开始,查看'MoreInfo:'案例

如果((l这是非常低效的,但它会起作用-此循环在将它们之间的任何内容打印为一个字符串之前查找第一个“(”和最后一个“)”(我还假设没有使用字符“-”):

输出为:

Name: Foo bar
UUID: <blah-blah-0101>
AnotherField: Some text
TieredField: (Number: 123;  Text: More Text;  YetAnotherTier: (Name: somename;  IP: 125.214.21.4) ;  )
NumericalData: 4
MoreInfo: Some Information
Name:Foo-bar
UUID:
另一个字段:一些文本
TieredField:(编号:123;文本:更多文本;YetAnotherTier:(名称:somename;IP:125.214.21.4);)
数字数据:4
更多信息:一些信息
下面是一个脚本

  • 读取原始的一行输入文件(input.txt)
  • 生成输出文件(output.txt)
更多:

  • 最初移除外部的两个大括号
  • 使用计数器计算内部大括号
  • 将IFS更改为读取所有字符(包括空格)


解析器生成器怎么样?例如,有很多。快速启动;示例看起来很有希望。除了去掉了空格外,它工作得很好。哎呀,刚刚看到@Kamil的答案,答案几乎相同。无论如何,正如上面的评论所建议的,使用解析器是非常明智的。
Name: Foobar
UUID: <blah-blah-0101>
AnotherField: Sometext
TieredField: (Number:123;Text:MoreText;YetAnotherTier:(Name:somename;IP:125.214.21.4);)
NumericalData: 4
MoreInfo: SomeInformation
t=''
n=0
oIFS=$IFS
IFS=';'
for f in $(sed -e 's/^(//' -e 's/) ;$//')
do
    if [[ $f = *'('* ]]; then
        t="${t}_ $f"
        let n++
    elif [[ $f = *')'* ]]; then
        t="${t}_ $f"
        let n--
        [[ $n -eq '0' ]] && echo ${t##_  }
    elif [[ $n -ne '0' ]]; then
        t="${t}_ $f"
    else
        echo ${f## }
    fi

done | IFS=$oIFS sed 's/_/;/g'
Name: Foo bar
UUID: <blah-blah-0101>
AnotherField: Some text
TieredField: (Number: 123;  Text: More Text;  YetAnotherTier: (Name: somename;  IP: 125.214.21.4) ;  )
NumericalData: 4
MoreInfo: Some Information
#!/bin/bash

WITHOUT_OUTER="`cat input.txt | cut -d"(" -f2- | rev | cut -d")" -f2- | rev`;"
PAIR=''
CNT=0
NEWLINE=0
OLD_IFS=$IFS
IFS=''
while read -n1 C
do
  if [ "$C" == '(' ]
  then
    CNT=$((CNT+1))
  elif [ "$C" == ')' ]
  then
    CNT=$((CNT-1))
  fi
  if [ $CNT -eq 0 ]
  then
    if [ "$C" == ';' ]
    then
      PAIR="$PAIR\n"
      NEWLINE=1
    fi
  elif [ "$C" == ';' ]
  then
    PAIR="$PAIR$C"
  fi
  if [ "$C" != ";" ]
  then
    if [ ! $NEWLINE -eq 1 ]
    then
      PAIR="$PAIR$C"
    else
      NEWLINE=0
    fi
  fi
done < <(echo $WITHOUT_OUTER)
echo -e "$PAIR" > output.txt
Name: Foo bar
UUID: <blah-blah-0101>
AnotherField: Some text
TieredField: (Number: 123; Text: More Text; YetAnotherTier: (Name: somename; IP: 125.214.21.4) ; )
NumericalData: 4
MoreInfo: Some Information