2016-05-26 8 views
3

Ich habe mit dem Ziehen bestimmter Werte aus einer sehr hässlichen CSV-Datei beauftragt.grep-ing für den Wert einer CSV-Datei

Die CSV ist in folgendem Format:

command1=value1, command2=value2, etc etc. 

Kein Problem bisher war ich für den Befehl grep-ing I benötigt und dann durch Schnitt kochend f 2 d ‚=‘ zurückzukehren nur die Wert.

Das Problem, das ich habe, ist eines der Felder ist Text und kann mehrere Werte haben, die auch durch Komma getrennt sind. Um eine weitere Kurve Ball hinzufügen, wenn (und nur dann) einen der Werte einen Raum darin hat, wird das Feld in doppelte Anführungszeichen eingeschlossen werden, damit der Wert, den ich bin auf der Suche zu ziehen sein könnte:

command=value,.. 
command=value1,value2,.. 
command="value 1",.. 
command="value 1, value 2",.. 

(wo. Andere Werte sind in der Protokolldatei ODER am Ende der Zeile)

Ich dachte, ich hätte es geknackt, indem ich einfach die Daten zwischen zwei Feldnamen mit grep -oP '(? < = command1 =) gezogen habe. = command2) 'und dann durch rev | cut -c 2- | rev.

Aber ich habe jetzt die Reihenfolge der Felder erscheinen nicht konsistent sind, so dass die Datei konnte herausgefunden:

command1=value1, command3=value3, command2=value2 

Wie kann ich den Wert von command2 erhalten, wenn es nicht in eingeschlossen werden kann oder doppelte Anführungszeichen, es kann auch Kommas enthalten. Ich habe Mühe zu sehen, wie es möglich ist, wie der Grep wissen wird, was ein Wertbruch ist und was das nächste Feld ist.

Jede Hilfe wird dankbar angenommen.

+0

Was Sie beschreiben, klingt trivial mit einem kleinen, einfachen awk Skript zu handhaben, aber Sie werden einige kurze, prüfbare Abtastwerteingang schreiben müssen und erwartete Ausgabe f oder wir können Ihnen helfen. –

Antwort

1

Im schlimmsten Fall (wenn beispielsweise , command2= im angegebenen Wert eines anderen Schlüssels vorkommen könnte), ist die einzige Möglichkeit wahrscheinlich, einen dedizierten Parser für dieses lästige Format zu schreiben. (Die Person zu töten, die damit auftrat, wird leider keine Probleme lösen und kann neue ergeben. Ich verstehe, dass es verlockend sein könnte, aber nicht.)

Für einen schnellen und schmutzigen Hack, vielleicht ist dies obwohl ausreichend,:

grep -oP '(^|,)command2=\K([^,"]+|"[^"]+")' 

Dies wird die doppelten Anführungszeichen halten, wenn der Feldwert angegeben wird, aber das shoud leicht zu beheben, wenn es unerwünscht ist. Der Wechsel zu einem besseren Werkzeug als grep könnte aber auch eine bessere Präzision bringen; hier ist eine sed Variante mit zusätzlicher Verankerung:

sed -n 's/^\(.*, \)*command2=\(\((^,"]*\)\|"\([^"]*\)"\)\(, .*\)*$/\4\5/p' 
1

I grep und sed kombinieren würde.Angenommen, Sie haben diesen Eingang in example.csv:

command1=value1, command2=value2, 
command1=value1, command2="value2, value3" 
command1=value1, command3=valu3 

Dann ist dieser Befehl ein:

grep 'command2=' example.csv | 
    sed -e 's/.*command2=//g' -e 's/^\([^"][^,]*\),.*$/\1/g' -e 's/^"\([^"]*\)".*$/\1/g' 

geben Sie dies:

value2 
value2, value3 

Erläuterung:

  • grep das Recht finden ZeilenDer erste Ausdruck in sed (d. die Tannen -e) entfernt alles vor dem
  • der zweite Ausdruck Angebote gewünschten Wert mit dem Fall ohne Anführungszeichen
  • die dritte Ausdruck behandelt den Fall mit Anführungszeichen

Bitte beachten Sie, dass CSV ein äußerst kompliziert Format. Diese Regex macht einige Annahmen, z.B. Befehl2 erscheint nur als Schlüssel. Wenn dieser csv nicht gut genug ist, dann würde ich eine echte Programmiersprache verwenden, die eine ausgereifte csv-Bibliothek hat.

+0

'grep | sed' kann sehr oft fruchtbar zu einem 'sed'-Skript umgestaltet werden. – tripleee

+0

Ist es /// p', wie in deiner Antwort? –

+1

'grep 'foo'' ist' sed'/foo/''aber in diesem Fall würde ich' -e'/command2 = /! D''am Anfang des Skripts setzen (nicht übereinstimmende Zeilen löschen). – tripleee

0

IDK wenn es ist, was Sie suchen oder nicht, aber diese Eingabedatei angegeben:

$ cat file 
command1=value1.1,command2=value2.1,value2.2,command3="value 3.1",command4="value 4.1, value 4.2" 

dieses awk GNU (für das vierte arg aufzuspalten()) Skript könnte sein, was Sie wollen:

$ cat tst.awk 
{ 
    delete(c2v) 
    split($0,f,/,?[^=,]+=/,s) 
    for (i=1; i in s; i++) { 
     gsub(/^,|=$/,"",s[i]) 
     print "populating command name to value array:", s[i], "->", f[i+1] 
     c2v[s[i]] = f[i+1] 
    } 
    print c2v["command2"] 
    print c2v["command4"] 
} 

$ awk -f tst.awk file 
populating command to value: command1 -> value1.1 
populating command to value: command2 -> value2.1,value2.2 
populating command to value: command3 -> "value 3.1" 
populating command to value: command4 -> "value 4.1, value 4.2" 
value2.1,value2.2 
"value 4.1, value 4.2" 

ändern Sie die Druckanweisungen zu Anzug, sollte es offensichtlich sein ...

Verwandte Themen