【问题标题】:Ignoring commas in attribute value while writing to csv file写入 csv 文件时忽略属性值中的逗号
【发布时间】:2016-03-17 22:17:02
【问题描述】:

我是 awk 的新手,并试图完成将 ldif 文件转换为 csv 的工作,为此我使用 awk 来解析和转换它。如果属性值中没有任何逗号,则下面的脚本工作正常。但是如果属性值中有逗号,则在 csv 中它会创建一个单独的列。 因此,如果属性值字段包含使用 awk 的逗号,则需要帮助来操作它。 下面是我正在使用的 awk 文件和示例数据。 AWK 脚本:-

#!/bin/awk -f
BEGIN {
        uid = ""
        omEntitytype = ""
        sn = ""
        givenName = ""
        initials = ""
        omUnit = ""
        departmentNumber = ""
        omCostCenter = ""
        title = ""
        omManager = ""
        omaffiliatedaccount = ""
        printf("USERID,USER_TYPE,USER_LASTNAME,USER_FIRSTNAME,USER_INITIAL,USER_UNIT,USER_DEPT,CHARGE_UNIT,USER_JOB_TITLE,SPONSOR_USERID,ACCOUNT_ID\n");
}
/^uid:/              {uid=$2}
/^omEntitytype:/       {omEntitytype=$2}
/^sn:/              {sn=$2}
/^givenName:/          {givenName=$2}
/^initials:/               {initials=$2}
/^omUnit:/              {omUnit=$2}
/^departmentNumber:/      {departmentNumber=$2}
/^omCostCenter:/      {omCostCenter=$2}
/^title:/ {title=$2}
/^omManager:/            {omManager=$2}
/^omaffiliatedaccount:/          {omaffiliatedaccount=$2}
/^dn/{
        if(uid != "") printf("%s,%s,%s,,%s,%s,%s,%s,%s,%s,%s,%s\n",uid,omEntitytype,sn,givenName,initials,omUnit,departmentNumber,omCostCenter,title,omManager,omaffiliatedaccount);
        uid = ""
        omEntitytype = ""
        sn = ""
        givenName = ""
        initials = ""
        omUnit = ""
        departmentNumber = ""
        omCostCenter = ""
        title = ""
        omManager = ""
        omaffiliatedaccount = ""
}
#capture last dn
END{
       if(uid != "") printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",uid,omEntitytype,sn,givenName,initials,omUnit,departmentNumber,omCostCenter,title,omManager,omaffiliatedaccount)
}

要传递给它的样本 ldif 数据

dn: uid=sample1,o=people,dc=om,DC=COM
uid: sample1
omEntitytype: Contingent, off-Site
sn: sample1 name1
givenName: sample1
initials: P
omUnit: 07
departmentNumber: 123
omCostCenter: 10
title: Analyst
omaffiliatedaccount: 12345

dn: uid=sample2,o=people,dc=om,DC=COM
uid: sample2
omEntitytype: Contingent, On-Site
sn: sample2 name2
givenName: sample2
initials: P
omUnit: 07
departmentNumber: 123
omCostCenter: 10
title: PLAT MGR, ENGINE,HYD,ELECT,DRIVES
omaffiliatedaccount: 12345

在示例数据中,我遇到了字段 omEntitytype 和标题的问题(填充到 csv 中的新列),因为它们的值是 ','。

任何帮助表示赞赏..在此先感谢。

【问题讨论】:

  • 您没有向我们展示预期的输出或告诉我们您希望如何处理数据中的逗号 - 转义它们?引用他们?替换它们?还有什么?编辑您的问题以显示给定输入的预期输出。

标签: awk


【解决方案1】:

只要数据中有名称 -> 值对,最好先构建一个类似的映射数组,然后按名称打印值:

$ cat tst.awk
BEGIN {
    OFS = ","
    numFlds = split("uid omEntitytype sn givenName initials omUnit departmentNumber omCostCenter title omManager omaffiliatedaccount",flds)
    print "USERID,USER_TYPE,USER_LASTNAME,USER_FIRSTNAME,USER_INITIAL,USER_UNIT,USER_DEPT,CHARGE_UNIT,USER_JOB_TITLE,SPONSOR_USERID,ACCOUNT_ID"
}
{
    name = value = $0
    sub(/:.*/,"",name)
    sub(/[^:]*:[[:space:]]*/,"",value)
    name2value[name] = value
}
!NF { prtRec() }
END { prtRec() }

function prtRec() {
    for (i=1; i<=numFlds; i++) {
        printf "\"%s\"%s", name2value[flds[i]], (i<numFlds?OFS:ORS)
    }
    delete name2value
}

$ awk -f tst.awk file
USERID,USER_TYPE,USER_LASTNAME,USER_FIRSTNAME,USER_INITIAL,USER_UNIT,USER_DEPT,CHARGE_UNIT,USER_JOB_TITLE,SPONSOR_USERID,ACCOUNT_ID
"sample1","Contingent, off-Site","sample1 name1","sample1","P","07","123","10","Analyst","","12345"
"sample2","Contingent, On-Site","sample2 name2","sample2","P","07","123","10","PLAT MGR, ENGINE,HYD,ELECT,DRIVES","","12345"

您实际上并没有告诉我们您希望如何处理逗号,上面引用了它们,但如果这不是您想要的,那么只需更改 prtRec() 以做您想做的任何事情,例如也许gsub(/,/,";",name2value[flds[i]])

请注意,如果您的输入数据没有丢失某些您想要输出的字段和/或如果您只是希望字段按照它们在输入中出现的顺序输出,那么上面的内容会简单一些。

【讨论】:

  • 谢谢莫顿..你的解决方案确实成功了..如果你有时间,你能解释一下你的逻辑吗..因为我是 awk 的新手,无法获得 suv 部分..再次感谢你的帮助莫顿。
  • 很棒的解决方案。正是我正在寻找的。谢谢@Ed Morton
【解决方案2】:

请原谅我的无知,但您能不能只使用不同的字符(例如“/”)来分隔多个标题?

【讨论】:

  • 嗨,对于 ldif 数据,我使用“:”作为区分属性名称和值的分隔符。所以我试图将其转换为 csv 文件。所以在使用 awk 进行转换时, omEntitytype 和 title 的属性值进入 csv 的新列,因为值中有一个 ',' ..所以在写入 csv 时需要帮助忽略逗号..
【解决方案3】:

您可以检查字段中是否存在“,”,您可以将其更改为另一个字符或引用该字段(这是标准的)

这是后一种情况的原型

echo "a:b,c:d" | 
awk -F: -v q='"' -v OFS=, '{for(i=1;i<=NF;i++)
                                 if(match($i,",")) $i=q $i q}1'

a,"b,c",d

【讨论】:

    【解决方案4】:

    我会这样做(需要 GNU awk)

    generate LDIF | gawk '
        function quote(str) {
            if (str ~ /,/) str = "\"" str "\""
            return str
        }
        function print_record() { 
            if (data["uid"] != "") 
                printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
                    quote(data["uid"]),
                    quote(data["omEntitytype"]),
                    quote(data["sn"]),
                    quote(data["givenName"]),
                    quote(data["initials"]),
                    quote(data["omUnit"]),
                    quote(data["departmentNumber"]),
                    quote(data["omCostCenter"]),
                    quote(data["title"]),
                    quote(data["omManager"]),
                    quote(data["omaffiliatedaccount"])
        }
        match($0, /^([^:]+): (.*)/, m) {data[m[1]] = m[2]}
        NF == 0 {print_record(); delete data}
        END {print_record()}
    '
    

    根据您的示例输入,此输出

    sample1,"Contingent, off-Site",sample1 name1,sample1,P,07,123,10,Analyst,,12345
    sample2,"Contingent, On-Site",sample2 name2,sample2,P,07,123,10,"PLAT MGR, ENGINE,HYD,ELECT,DRIVES",,12345
    

    【讨论】:

    • 是的。我会更新的。起初我确实使用了一个正则表达式,事后看来,它更健壮
    • 很好地使用了 gawk match() !
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-03
    • 1970-01-01
    • 2017-09-14
    • 2017-08-08
    • 2016-03-08
    • 1970-01-01
    相关资源
    最近更新 更多