【问题标题】:splitting string with "- " delimiter using sed not working使用 sed 用“-”分隔符分割字符串不起作用
【发布时间】:2021-12-08 10:43:37
【问题描述】:

我有下面的字符串,每一行都用换行符作为输入字符串

string="name: MAIN_ROLE
description: ROLE DESCRIPTION
readOnly: 
roleReferences:
- roleTemplateAppId: app1
  roleTemplateName: template2
  name: Name1
- roleTemplateAppId: app2
  roleTemplateName: template2
  name: Name2
"

我喜欢将 YAML 字符串打印成逗号分隔的字符串,结果如下。输入字符串在“-”之后可以有任意数量的组件,这会产生新记录,但 MAIN_ROLE 值保持相同的第一列:

MAIN_ROLE,Name1,template1,app1
MAIN_ROLE,Name2,template2,app2

我尝试下面的代码用“-”分割行,但我没有得到正确的结果

echo "$a" | sed -n $'/^- $/,/^- $/p' <<< $string

【问题讨论】:

  • 不要使用sed解析JSON。
  • 我认为字符串是 YAML 而不是 JSON。我不认为 Bash 有任何内置的东西可以让我解析这些类型的字符串。我想我将不得不拆分以“-”分隔的字符串并存储在数组中并使用“:”进一步拆分以获得最终结果,但我坚持第一步。

标签: shell sed split


【解决方案1】:

你可以这样使用awk

awk 'NR==1{a=$2;cnt=0} /^-/{rta[cnt]=$3;getline;rtn[cnt]=$2; getline; n[cnt]=$2;cnt++} END{ for(i=0;i<cnt;i++) { print a","n[i]","rtn[i]","rta[i] } }' file > outputfile

online demo

#!/bin/bash
string="name: MAIN_ROLE
description: ROLE DESCRIPTION
readOnly:
roleReferences:
- roleTemplateAppId: app1
  roleTemplateName: template1
  name: Name1
- roleTemplateAppId: app2
  roleTemplateName: template2
  name: Name2
"
awk 'NR==1{               # When on Line 1
    a=$2;cnt=0            # Set a (main name) and cnt (counter) vars
}
/^-/{                     # When line starts with -
    rta[cnt]=$3; getline; # Add role template app ID to rta array, read next line
    rtn[cnt]=$2; getline; # Add role template name to rtn array, read next line
    n[cnt]=$2;cnt++       # Add name to n array, increment the cnt variable
}
END{                      # When the file processing is over
    for(i=0;i<cnt;i++) {  # Iterate over the found values and... 
        print a","n[i]","rtn[i]","rta[i]  # print them
    }
}' <<< "$string"

# => MAIN_ROLE,Name1,template1,app1
#    MAIN_ROLE,Name2,template2,app2

【讨论】:

  • 我喜欢在 awk 中的文件中写入最后一行,但我的 fullfilepath 外部变量无法识别。我试过 printf a","n[i]","rtn[i]","rta[i] >> "$fullfilepath" 但没有用。如何将 $fullfilepath 传递给 awk 命令以便它可以写入给定文件?
  • @LordOfTheRing 您需要像这样将变量传递给 awk:awk -v fullfilepath="$fullfilepath"...
  • 我这样做了,但仍然无法正常工作: awk -v fullfilepath="$fullfilepath" 'NR==1 { a=$2;cnt=0 } /^-/{ rta[cnt] =$3;获取线; rtn[cnt]=$2;获取线; n[cnt]=$2;cnt++ } END{ # 文件处理结束时 for(i=0;i> "$fullfilepath" # 打印到文件 } }'
  • @LordOfTheRing It works 在我这边。但是现在我意识到您没有正确使用它,只需使用awk 'NR==1{a=$2;cnt=0} /^-/{rta[cnt]=$3;getline;rtn[cnt]=$2; getline; n[cnt]=$2;cnt++} END{ for(i=0;i&lt;cnt;i++) { print a","n[i]","rtn[i]","rta[i] } }' &lt;&lt;&lt; "$string" &gt; "$fullfilepath"。逐行更新文件效率不高。除非您有一些特定要求,例如将每一行写入单独的文件,否则我宁愿将 awk 输出直接重定向到文件。
  • 它有效,我在 awk 中使用 "$fullfilepath" 而不是 fullfilepath 。谢谢
【解决方案2】:

使用您展示的示例,您可以尝试关注awk 程序。这可以在很少的条件下简单地完成,而不是使用阵列系统。

awk '
BEGIN{ OFS="," }
/roleTemplateAppId/{
  if(name && template){
    print "MAIN_ROLE",name,template,$NF
  }
  name=template=""
}
/roleTemplateName:/{
  template=$NF
  next
}
/name:/{
  name=$NF
}
END{
  if(name && template){
     print "MAIN_ROLE",name,template,$NF
  }
}
'  Input_file

说明:为上述添加详细说明。

awk '                                     ##Starting awk program from here.
BEGIN{ OFS="," }                          ##Setting OFS to , in BEGIN section.
/roleTemplateAppId/{                      ##Check if line contains roleTemplateAppId then do following.
  if(name && template){                   ##check if name and template is SET then do following.
    print "MAIN_ROLE",name,template,$NF   ##Printing MAINE_ROLE name, template and last field value here.
  }
  name=template=""                        ##Nullifying name and template here.
}
/roleTemplateName:/{                      ##Check if roleTemplateName: is found in current line then do following.
  template=$NF                            ##Setting template to last field of current line.
  next                                    ##next will skip all further statements from here.
}
/name:/{                                  ##Checking condition if line contains name: then do following.
  name=$NF                                ##Setting name value as last field name.
}
END{                                      ##Starting END block of this program from here.
  if(name && template){                   ##check if name and template is SET then do following.
     print "MAIN_ROLE",name,template,$NF  ##Printing MAINE_ROLE name, template and last field value here.
  }
}
'  Input_file                             ##Mentioning Input_file name here. 

【讨论】:

  • 非常感谢您的出色逻辑和解释。我在 awk 命令方面几乎是文盲。
猜你喜欢
  • 2013-08-16
  • 1970-01-01
  • 2013-05-03
  • 2021-06-12
  • 2020-07-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多