【问题标题】:text processing using awk or sed使用 awk 或 sed 进行文本处理
【发布时间】:2021-07-19 21:52:24
【问题描述】:

我尝试使用下面的 awk 和 sed 实用程序来解析仅在引号内的匹配模式,但它占用了整行。

代码:

awk '/''/ { print }' parse.xml

输入

<msg time='005-04:00' org_id='oracle' comp_id='lsnr'
 type='UNKNOWN' level='16' host_id='localhost'
 host_addr='127.0.0.1' pid='6322'>
 <txt>05:34:54 * service_update * orclcdb * 0
 </txt>
</msg>

期待输出

Time      Org_id  Comp_id Type     Level Host_id   Host_addr  Pid  Txt
-----     ------- ------- -----    ----- -------   ---------  ---- --- 
005-04:00 oracle  lsnr    UNKNOWN  16    localhost 127.0.0.1  6322 05:34:54 * service_update * orclcdb * 0

【问题讨论】:

  • 我认为使用 xml 解析器解析 xml 会更好。至于在单引号之间取出内容,您可以使用 awk 将单引号拆分并打印出每隔一列:awk -F"'" '{for (i=2;i&lt;=NF;i=i+2){print $i}}' 如果您的引号字符串跨越多行,或者如果您还需要保留 &lt;msg&gt;,这会变得更丑陋标记它们作为单行包含在其中,并且您还需要获取 &lt;txt&gt; 标记的内部文本...您添加的每一点都会使它变得越来越丑陋,直到您使用 xml 解析器来获得您的恢复理智。
  • 您的问题标题text processing using awk or sed 是不好的,正如它告诉我们的那样,任何有类似问题的人将来都会在档案中搜索解决方案,而与您的实际问题无关。请解决这个问题。
  • 你认为 shell 会如何解析 awk '/''/ { print }' parse.xml,为什么你认为它与 awk '// { print }' parse.xml 有什么不同?

标签: bash shell unix awk sed


【解决方案1】:

对您引用的字符串中可能包含的内容做出多个假设,对于您显示的输入,我们可以使用 GNU awk 执行以下操作,用于多字符 RS、FPAT、gensub() 和 \s/\S 速记:

$ cat tst.awk
BEGIN {
    RS = "</msg>"
    OFS = "\t"
    FPAT = "[[:alnum:]_]+=|'[^']*'|<txt>.*</txt>"
}
{ $0 = gensub(/<(txt)>(.*\S)\s*<\/txt>/,"\\1='\\2'",1) }
FNR == 1 {
    for (i=1; i<NF; i+=2) {
        $i = toupper(substr($i,1,1)) substr($i,2)
        sub(/=/,"",$i)
        printf "%s%s", $i, (i<(NF-1) ? OFS : ORS)
    }
    for (i=1; i<NF; i+=2) {
        gsub(/./,"-",$i)
        printf "%s%s", $i, (i<(NF-1) ? OFS : ORS)
    }
}
{
    for (i=2; i<=NF; i+=2) {
        gsub(/^'|'$/,"",$i)
        printf "%s%s", $i, (i<NF ? OFS : ORS)
    }
}

$ awk -f tst.awk file | column -s$'\t' -t
Time       Org_id  Comp_id  Type     Level  Host_id    Host_addr  Pid   Txt
----       ------  -------  ----     -----  -------    ---------  ---   ---
005-04:00  oracle  lsnr     UNKNOWN  16     localhost  127.0.0.1  6322  05:34:54 * service_update * orclcdb * 0

【讨论】:

  • 没有文件输入,打印选项是6个字段,但必填字段是9个。
  • 您的示例 awk '/''/ { print }' parse.xml 专门显示了文件输入,但无论如何,文件输入或管道输入或标准输入输入 - 只需像使用任何其他工具一样在文件、管道或标准输入上运行脚本。我不知道the print option is for 6 fields but the required fields are 9 是什么意思。
  • @DevopsDevelop 这是否解决了您的问题?如果不是 - 它会从您提供的示例输入中准确产生您所要求的输出,那么它以何种方式不完全符合您的要求?如果您问题中的示例有误,或者您有其他/不同的要求,请更新您的问题以进行更正。
【解决方案2】:

跟进我的评论。我认为使用 sed 或 awk 解析 XML 是个坏主意,尽管它已经完成了。更好的是python,它内置在xml库中。像下面这样的东西可以解决问题:

import csv
import xml.etree.ElementTree as ET

# load xml from file
tree = ET.parse('test.xml')
root = tree.getroot()

#open a text file and write out the header
with open("test.csv", 'w') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=['time', 'org_id', 'comp_id', 'type', 'level', 'host_id', 'host_addr', 'pid', 'txt'])
    writer.writeheader()
    
    #loop through every msg at root
    for msg in root.findall('.'):    
        #dump the msg tag's attributes to a dictionary named "row"    
        row=msg.attrib
        #add to the dict for the <txt> tag's text
        row['txt']= msg[0].text
        #write it out
        writer.writerow(row)

如果您遇到传入 XML 的任何更改或复杂性,例如如果有多个 msg 标记应该写成它自己的行,或者如果有多个消息中的一个&lt;txt&gt; 标记,或者您的属性文本是否应该跨越多行。

【讨论】:

【解决方案3】:

您可以使用ruby 作为轻量级 XML 解析器来执行此操作:

ruby -r nokogiri -e '
BEGIN{h={};a=[]}
doc=Nokogiri::XML($<.read)
doc.xpath("//msg/@* | //msg/*").each{ |e| 
    h[e.name]=e.value rescue h[e.name]=e.text.rstrip()}
h.map{|k,v| 
    w=[k.length, v.length].max; a.append([k.center(w," "),"-"*w,v.center(w," ")])}
[0,1,2].each{|i| a.each{|e| printf("%s ", e[i])}; puts}
' file  

打印:

  time    org_id comp_id  type   level  host_id  host_addr pid                    txt                   
--------- ------ ------- ------- ----- --------- --------- ---- --------------------------------------- 
005-04:00 oracle  lsnr   UNKNOWN  16   localhost 127.0.0.1 6322 05:34:54 * service_update * orclcdb * 0 

您也可以使用xmlstarlet 来处理XML:

xmlstarlet sel -T -t -m "//msg/@* | //msg/*" -v "concat(name(),'=',.)" -n file
time=005-04:00
org_id=oracle
comp_id=lsnr
type=UNKNOWN
level=16
host_id=localhost
host_addr=127.0.0.1
pid=6322
txt=05:34:54 * service_update * orclcdb * 0

然后使用awk 格式化:

xmlstarlet sel -T -t -m "//msg/@* | //msg/*" -v "concat(name(),'=',.)" -n file |
awk  '{arr[++n]=$0}
END{for (i=1;i<=n;i++) {
    split(arr[i], parts, "=")
    w=(length(parts[1])>length(parts[2]) ? length(parts[1]) : length(parts[2]))
    l1=l1 (sprintf("%-*s", w, parts[1]) " ")
    l2=l2 (gensub(/ /, "-", "g", sprintf("%*s", w, "")) " ")
    l3=l3 (sprintf("%-*s", w, parts[2]) " ")
    }
    printf("%s\n%s\n%s\n",l1,l2,l3)
}'

打印:

time      org_id comp_id type    level host_id   host_addr pid  txt                                      
--------- ------ ------- ------- ----- --------- --------- ---- ---------------------------------------  
005-04:00 oracle lsnr    UNKNOWN 16    localhost 127.0.0.1 6322 05:34:54 * service_update * orclcdb * 0 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-10
    相关资源
    最近更新 更多