Bash 使用shell解析CSV文件

Bash 使用shell解析CSV文件,bash,shell,unix,awk,Bash,Shell,Unix,Awk,我的shell有点生锈了,因此我非常感谢在解析以下数据时提供一些帮助 输入文件中的每一行都包含以逗号分隔的数据 [name, record_timestamp, action, field_id, field_name, field_value, number_of_fields] 这些行是创建或更新有关人员信息的说明。例如,第一行表示将创建John Smith,下面6行将包含关于他的信息 字段标识号始终表示同一字段 input.csv John Smith,2017-03-03 11:56:

我的shell有点生锈了,因此我非常感谢在解析以下数据时提供一些帮助

输入文件中的每一行都包含以逗号分隔的数据

[name, record_timestamp, action, field_id, field_name, field_value, number_of_fields]
这些行是创建或更新有关人员信息的说明。例如,第一行表示将创建John Smith,下面6行将包含关于他的信息

字段标识号始终表示同一字段

input.csv

John Smith,2017-03-03 11:56:02,create,,,,6
,,,,1,BIRTH_DATE,1985-02-16,,
,,,,2,BIRTH_CITY,Portland,,
,,,,3,SEX,Male,,
,,,,5,CITY,Seattle,,
,,,,7,EMPLOYER,Microsoft,,
,,,,9,MARRIED,Yes,,
Susan Anderson,2017-03-01 12:09:36,create,,,,8
,,,,1,BIRTH_DATE,1981-09-12,,
,,,,2,BIRTH_CITY,San Diego,,
,,,,3,SEX,Female,,
,,,,5,CITY,Palo Alto,,
,,,,7,EMPLOYER,Facebook,,
,,,,8,SALARY,5612,,
,,,,9,MARRIED,No,,
,,,,10,TELEPHONE,5107586290,,
Brad Bradly,2017-02-29 09:15:12,update,,,,3
,,,,3,SEX,Male,,
,,,,7,EMPLOYER,Walmart,,
,,,,9,MARRIED,No,,
Sarah Wilson,2017-02-28 16:21:39,update,,,,5
,,,,2,BIRTH_CITY,Miami,,
,,,,3,SEX,Female,,
,,,,7,EMPLOYER,Disney,,
,,,,8,SALARY,5110,,
,,,,9,MARRIED,Yes,,
我想将每个人解析为逗号分隔的字符串,如下所示:

name,birth date,birth city,sex,employer,salary,marrage status,record_timestamp
但是,我们应该只在出生日期和出生城市两个字段或雇主和工资两个字段都可用时输出这样的字符串。否则,只需将其留空(参见下面的示例)

如果我们的输入高于输出,那么输出应该是

John Smith,1985-02-16,Portland,Male,,,Yes,2017-03-03 11:56:02
Susan Anderson,1981-09-12,San Diego,Female,Facebook,5612,No,2017-03-01 12:09:36
Sarah Wilson,,,Female,Disney,5110,Yes,2017-02-28 16:21:39
我想我应该按照下面的思路做些事情。但是,我不知道如何实现内部循环,或者是否有其他方法可以继续

#!/bin/bash
IFS=','
cat test.txt | while read -a outer
do
    echo ${outer[0]}
    #...
done

提前感谢您的建议

awk
救援

awk -F, 'function pr(a) {if(!(7 in a && 8 in a)) a[7]=a[8]=""; 
                         if(!(1 in a && 2 in a)) a[1]=a[2]=""; 
                         for(i=0;i<=10;i++) printf "%s,",a[i]; 
                         printf "%s\n", a["ts"]} 
         NR>1 && $1!="" {pr(a); delete a}
         $1!=""         {a[0]=$1; a["ts"]=$2} 
         $1==""         {a[$5]=$7}
         END            {pr(a)}' file

awk
救援

awk -F, 'function pr(a) {if(!(7 in a && 8 in a)) a[7]=a[8]=""; 
                         if(!(1 in a && 2 in a)) a[1]=a[2]=""; 
                         for(i=0;i<=10;i++) printf "%s,",a[i]; 
                         printf "%s\n", a["ts"]} 
         NR>1 && $1!="" {pr(a); delete a}
         $1!=""         {a[0]=$1; a["ts"]=$2} 
         $1==""         {a[$5]=$7}
         END            {pr(a)}' file

使用
awk
或类似工具

while IFS=, read -r name timestamp action f_id f_name f_value nr_fields; do
   if [ -n "${name}" ]; then
      # proces startrecord, store the fields you need for the next line
   else
      # process next record
   fi
done < test.txt
当IFS=,read-r name timestamp action f_id f_name f_value nr_字段时;做
如果[-n“${name}”];然后
#处理startrecord,存储下一行所需的字段
其他的
#处理下一条记录
fi
完成
使用
awk
或类似工具

while IFS=, read -r name timestamp action f_id f_name f_value nr_fields; do
   if [ -n "${name}" ]; then
      # proces startrecord, store the fields you need for the next line
   else
      # process next record
   fi
done < test.txt
当IFS=,read-r name timestamp action f_id f_name f_value nr_字段时;做
如果[-n“${name}”];然后
#处理startrecord,存储下一行所需的字段
其他的
#处理下一条记录
fi
完成
避免像瘟疫一样的IFS黑客攻击。它们是丑陋的东西


使用-d选项读取以指定逗号作为分隔符。

避免类似瘟疫的IFS攻击。它们是丑陋的东西


使用-d选项读取以指定逗号作为分隔符。

UNIX shell是一种环境,可以在其中使用一种语言调用UNIX工具(并操作文件和进程),以对这些调用进行排序

处理文本的标准UNIX工具是awk:

$ cat tst.awk
BEGIN {
    numFlds=split("name BIRTH_DATE BIRTH_CITY SEX EMPLOYER SALARY MARRIED timestamp",nr2name)
    FS=OFS=","
}
$1 != "" {
    prtRec()
    rec["name"] = $1
    rec["timestamp"] = $2
    next
}
{ rec[$6] = $7 }
END { prtRec() }

function prtRec(        fldNr) {
    if ( ((rec["BIRTH_DATE"] != "") && (rec["BIRTH_CITY"] != "")) ||
         ((rec["EMPLOYER"] != "") && (rec["SALARY"] != "")) ) {
        for (fldNr=1; fldNr<=numFlds; fldNr++) {
            printf "%s%s", rec[nr2name[fldNr]], (fldNr<numFlds ? OFS : ORS)
        }
    }
    delete rec
}

$ awk -f tst.awk file
John Smith,1985-02-16,Portland,Male,Microsoft,,Yes,2017-03-03 11:56:02
Susan Anderson,1981-09-12,San Diego,Female,Facebook,5612,No,2017-03-01 12:09:36
Sarah Wilson,,Miami,Female,Disney,5110,Yes,2017-02-28 16:21:39
$cat tst.awk
开始{
numFlds=split(“姓名出生日期出生城市性别雇主工资已婚时间戳”,nr2name)
FS=OFS=“,”
}
$1 != "" {
prtRec()
记录[“名称”]=1美元
rec[“时间戳”]=2美元
下一个
}
{rec[$6]=$7}
结束{prtRec()}
功能prtRec(fldNr){
如果((rec[“出生日期”!=”)和&(rec[“出生城市”!=”)||
((rec[“雇主”!=”)&&(rec[“工资”!=”)){

对于(fldNr=1;fldNr),UNIX shell是一种环境,在该环境中,可以使用一种语言来调用UNIX工具(以及操作文件和进程),从而对这些调用进行排序

处理文本的标准UNIX工具是awk:

$ cat tst.awk
BEGIN {
    numFlds=split("name BIRTH_DATE BIRTH_CITY SEX EMPLOYER SALARY MARRIED timestamp",nr2name)
    FS=OFS=","
}
$1 != "" {
    prtRec()
    rec["name"] = $1
    rec["timestamp"] = $2
    next
}
{ rec[$6] = $7 }
END { prtRec() }

function prtRec(        fldNr) {
    if ( ((rec["BIRTH_DATE"] != "") && (rec["BIRTH_CITY"] != "")) ||
         ((rec["EMPLOYER"] != "") && (rec["SALARY"] != "")) ) {
        for (fldNr=1; fldNr<=numFlds; fldNr++) {
            printf "%s%s", rec[nr2name[fldNr]], (fldNr<numFlds ? OFS : ORS)
        }
    }
    delete rec
}

$ awk -f tst.awk file
John Smith,1985-02-16,Portland,Male,Microsoft,,Yes,2017-03-03 11:56:02
Susan Anderson,1981-09-12,San Diego,Female,Facebook,5612,No,2017-03-01 12:09:36
Sarah Wilson,,Miami,Female,Disney,5110,Yes,2017-02-28 16:21:39
$cat tst.awk
开始{
numFlds=split(“姓名出生日期出生城市性别雇主工资已婚时间戳”,nr2name)
FS=OFS=“,”
}
$1 != "" {
prtRec()
记录[“名称”]=1美元
rec[“时间戳”]=2美元
下一个
}
{rec[$6]=$7}
结束{prtRec()}
功能prtRec(fldNr){
如果((rec[“出生日期”!=”)和&(rec[“出生城市”!=”)||
((rec[“雇主”!=”)&&(rec[“工资”!=”)){

对于(fldNr=1;fldNr CSV文件格式一开始是一个非常松散的标准,它比简单地用逗号拆分要复杂得多。为什么不使用一种带有适当CSV模块的脚本语言呢?它们都有。如果您的输入确实是完全规则的,那么Awk脚本会简单得多,可读性更高,速度更快得多SV文件格式从一开始就是一个非常松散的标准,它比简单地用逗号分割要复杂得多。为什么不使用带有适当CSV模块的脚本语言呢?它们都有。如果您的输入确实是完全规则的,那么Awk脚本会简单得多,可读性更高,速度也更快。谢谢您的回答。非常有帮助!AWK是前进的道路!:)谢谢你的回答。非常有帮助!AWK是前进的道路!:)谢谢你的回答。的确,AWK是前进的道路!:)谢谢你的回答。的确,AWK是前进的道路!:)