2017-03-08 116 views
0

我的外壳有点生锈,所以我非常感谢在解析下列数据时的一些帮助。使用外壳解析CSV文件

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

[name, record_timestamp, action, field_id, field_name, field_value, number_of_fields] 

这些行是用于创建或更新有关人员信息的说明。例如,第一行表示约翰史密斯将被创建,以下6行将包含关于他的信息。

field_id编号始终代表相同的字段。

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 

在此先感谢您的任何建议!

+1

CSV文件格式是一个非常宽松的标准,比起简单的逗号分隔要复杂得多。为什么不在适当的CSV模块中使用脚本语言?他们*全部*拥有它们。 – tadman

+1

如果您的输入确实是完全正常的,那么Awk脚本会更简单,更易读,并且速度更快。 – tripleee

回答

2

UNIX shell是一种可以使用语言调用UNIX工具(并处理文件和进程)以排序这些调用的环境。 It is NOT a tool to manipulate text

标准的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 

你有包括名称+值数据的记录,像你做的任何时间,是目前为止最简单导致的办法,最清晰,最强大的,并且最简单的增强/调试代码是首先填充包含名称索引值的数组(上面的rec[])。一旦你有了这个数组,通过他们的名字来打印和/或操作内容是很简单的。

+1

谢谢你的回答。很有帮助! AWK是要走的路! :) – DoubleTrouble

1

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 

这应该涵盖一般情况和条件字段。您可能需要过滤掉其他不需要的字段。

这将打印您的输入

John Smith,1985-02-16,Portland,Male,,Seattle,,,,Yes,,2017-03-03 11:56:02 
Susan Anderson,1981-09-12,San Diego,Female,,Palo Alto,,Facebook,5612,No,5107586290,2017-03-01 12:09:36 
Brad Bradly,,,Male,,,,,,No,,2017-02-29 09:15:12 
Sarah Wilson,,,Female,,,,Disney,5110,Yes,,2017-02-28 16:21:39 
+0

谢谢你的回答。的确,AWK是要走的路! :) – DoubleTrouble

1

使用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 
0

避免IFS黑客像瘟疫。他们是丑陋的东西。

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