2017-03-08 99 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選項玩請閱讀以指定逗號作爲分隔符。