ubuntuusers.de

Bash, in bestimmte Stelle einer Zeile einer csv Datei, einen Wert schreiben

Status: Ungelöst | Ubuntu-Version: Ubuntu 19.10 (Eoan Ermine)
Antworten |

Theo.Spengler

Anmeldungsdatum:
4. Januar 2019

Beiträge: 57

Nach dem ich bei einer anderen Frage von mir, größer als erhoffbaren Erfolg bei der Beantwortung hatte, will ich es heute gleich noch einmal, mit einer für mich vom Schwierigkeitsgrad lösbaren Frage versuchen.

In der folgenden Frage haben mir dingsbums und vor allem rklm zu zwei Möglichkeiten verholfen, aus einer bestimmte Spalte einer bestimmten Zeile einer csv Datei einen Wert aus zu lesen:

https://forum.ubuntuusers.de/topic/zeile-und-stelle-von-csv-datei-in-bash-ansprec/

Die beiden gefundenen Varianten sehen wie folgt aus:

Variante 1, in der die gewünschte Zelle per Zeilennummer und Spaltennummer angesrochen wird (hier Zeile 2, Spalte 3):

1
2
3
4
5
6
$ cat datei.csv
spalte1, spalte2, spalte3, spalte4, spalte5
Lisa, Simpson, London, Handball, lesen
Bad, Simpson, Paris, Fussball, schreiben
$ awk -vln=2 -vcol=3 -F ', ' 'NR == ln {print $col;exit 0}' datei.csv 
London

Variante 2, in der die gewünschte Zelle, per Zeilennummer und Spaltenüberschrift angesprochen wird (hier Zeile 2,

1
2
3
4
5
6
7
8
$ cat datei2.csv 
vorname, nachname, stadt, sportart, hobby
Lisa, Simpson, London, Handball, lesen
Bad, Simpson, Paris, Fussball, schreiben
$ awk -vln=2 -vcol=stadt -F ', ' 'NR == 1 {for (i=1;i<=NF;++i) if ($i==col)idx=i;if(!idx)exit 1} NR == ln {print $idx;exit 0}' datei2.csv
London
$ awk -vln=2 -vcol=fehlt -F ', ' 'NR == 1 {for (i=1;i<=NF;++i) if ($i==col)idx=i;if(!idx)exit 1} NR == ln {print $idx;exit 0}' datei2.csv
$ 

Was ich nun versuche zu finden, sind idealer Weise, die beiden oben benannten Varianten zum lesen eines Wertes, nun in umgekehrten Richtung, in der man jeweils einen Wert in eine bestimmte Zelle einer Zeile schreiben kann.

Meine bisherigen Recherchen waren hierzu praktisch im wesentlichen ohne für mich erkennbare Ansatzpunkte. Als mehr oder weniger bash Laie, fürchte ich, das man keine Zellen einer csv Datei einzeln beschreiben kann und in irgend einer Art und Weise, immer die komplette Datei neu schreiben muß.

Um die Comunity nicht all zu sehr einseitig zu beanspruchen, habe ich mich heute ein wenig an der Beantwortung leichterer unbeantworteter Fragen versucht:

https://forum.ubuntuusers.de/unanswered/

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17604

Wohnort: Berlin

Mit awk geht es sicher auch, aber da bin ich nicht so firm.

1
2
3
4
sed -r '2s/(([^,]*,){2})[^,]*,(.*)/\1 Rom,\3/' spaltenedit.csv 
spalte1, spalte2, spalte3, spalte4, spalte5
Lisa, Simpson, Rom, Handball, lesen
Bad, Simpson, Paris, Fussball, schreiben

Erklärung:

Das -r braucht man, um nicht so viele Sonderzeichen maskieren zu müssen. 2s bedeutet, dass auf Zeile 2 operiert werden soll mit einem Substitutionsbefehl (Ersetzung). Dann kommt im ersten Paar von /.../ das, was gesucht werden soll, im zweiten wodurch es ersetzt werden soll.

Dann arbeiten wir uns bei den Klammern von innen nach außen: [^,] Das Caret negiert in eckigen Klammern eine Zeichengruppe, hier nur das Komma. Also ist [^,]*, eine Zeichenkette aus beliebig vielen Nichtkommas, gefolgt von einem Komma, ergo der Inhalt von Spalte 1. Die runde Klammer ([^,]*,) sagt, dass wir das als Ausdruck betrachten wollen, mit den geschweiften ([^,]*,){2} und der Zahl 2 sagen wir, wir wollen 2 solche Ausdrücke, die runden Klammern drumrum (([^,]*,){2}) fassen das wieder als einen Ausdruck zusammen.

Danach folgt das gleiche Muster [^,]*, für Spalte 3 nochmal. Diesmal interessiert es uns nicht als Ausdruck, weil wir es wegfallen lassen wollen und durch was anderes ersetzen, dahinter eine beliebige Zeichenkette (.*) die uns wieder interessiert (Spalte 4 ff.).

Das ersetzen wir durch den Ausdruck "\1 Rom,\3", wobei \1 für den ersten, \3 für den dritten Ausdruck steht. Geschachtelte runde Klammern zählen von außen nach innen, also hier für die 2 Spalten 1 und 2. \2 stünde nur für die Spalte 1, das erste innere Match. Das Blank vor "Rom" nicht vergessen und das Komma und \3 für den Rest der Zeile.

Wenn bekannt ist, dass es London in Spalte 3 ist, und London nicht in einer anderen Spalte stehen kann, könnte man auch sehr viel leichter so schreiben:

1
sed -r '2s/London/Rom/' spaltenedit.csv 

Will man die Ausgabe nicht nur auf dem Bildschirm sehen, sondern in einer Datei:

1
sed -r '2s/London/Rom/' spaltenedit.csv > spaltenedit-mit-Rom.csv 

Soll die alte Datei überschrieben werden:

1
sed -ri '2s/London/Rom/' spaltenedit.csv

Soll die alte Datei gesichert werden:

1
sed -ri.bak '2s/London/Rom/' spaltenedit.csv

Sicherheitskopie ist dann: spaltenedit.csv.bak

Theo.Spengler

(Themenstarter)

Anmeldungsdatum:
4. Januar 2019

Beiträge: 57

user_unknown schrieb:

Mit awk geht es sicher auch ...

Das ist schon einmal spannend. Das scheint erst einmal die Annahme zu untermauern, das immer die gesammte Datei neu auf die Platte geschrieben wird.

Für den universellen Einsatz wäre es genial, wen man die Stelle an den eine neuer oder auch noch nicht existierende Wert in die csv Datei geschrieben werden soll, per Zeilennummer und wahlweise per Spaltennummer und Spaaltenüberschrift an geben könnte.

Zwischenergebnis:

Wie das mit awk geht, steht möglicher Weise unter den folgenden Links:

* https://www.gnu.org/software/gawk/manual/gawk.html#Simple-Sed

* https://www.gnu.org/software/gawk/manual/gawk.html#String-Functions

* https://www.cyberciti.biz/faq/awk-find-and-replace-fields-values/

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13174

Theo.Spengler schrieb:

user_unknown schrieb:

Mit awk geht es sicher auch ...

Das ist schon einmal spannend. Das scheint erst einmal die Annahme zu untermauern, das immer die gesammte Datei neu auf die Platte geschrieben wird.

Das muss es ja, mindestens der Inhalt nach der ersten Stelle mit Ersetzung, bei der der eingesetzte Text eine andere Länge hat als der ursprüngliche muss neu geschrieben werden. Einfacher ist es, gleich die gesamte Datei neu zu schreiben.

Für den universellen Einsatz wäre es genial, wen man die Stelle an den eine neuer oder auch noch nicht existierende Wert in die csv Datei geschrieben werden soll, per Zeilennummer und wahlweise per Spaltennummer und Spaaltenüberschrift an geben könnte.

Ja, das machst Du halt wie in dem anderen Thema. Zur Illustration, wie das mit awk gehen kann:

1
2
3
$ echo -e 'abc,def,ghi\njkl,mno,pqr' | awk -F , -v OFS=, 'NR==1{$2="FOO"} {print}'
abc,FOO,ghi
jkl,mno,pqr

Man weist einfach an eine Feldvariable zu und gibt dann wieder die ganze Zeile aus.

Zwischenergebnis:

Wie das mit awk geht, steht möglicher Weise unter den folgenden Links:

* https://www.gnu.org/software/gawk/manual/gawk.html#Simple-Sed

* https://www.gnu.org/software/gawk/manual/gawk.html#String-Functions

Viel zu umständlich.

* https://www.cyberciti.biz/faq/awk-find-and-replace-fields-values/

Und gsub() honoriert ja nicht Feldgrenzen, wenn Du das nicht explizit in den Regex einbaust. Und Du willst ja einen gesamten Feldwert einfügen.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17604

Wohnort: Berlin

Theo.Spengler schrieb:

user_unknown schrieb:

Mit awk geht es sicher auch ...

Das ist schon einmal spannend. Das scheint erst einmal die Annahme zu untermauern, das immer die gesammte Datei neu auf die Platte geschrieben wird.

Ja. Datenbanken machen das anders, oft zum Preis begrenzter Feldlängen und auf Kosten von Platzverschwendung bei leeren oder kurzen Inhalten.

Für den universellen Einsatz wäre es genial, wen man die Stelle an den eine neuer oder auch noch nicht existierende Wert in die csv Datei geschrieben werden soll, per Zeilennummer und wahlweise per Spaltennummer und Spaaltenüberschrift an geben könnte.

Auch das klingt so, als brauchtest Du eigentlich eine Datenbank. Allerdings haben auch CSV-Dateien große Vorteile.

Besser wäre es gewesen, die Frage gleich so zu stellen, dass Du nach einer wahlweisen Möglichkeit suchst, Spalteninhalte bestimmter Zeilen zu ändern.

Die Frage wäre aber auch woher Du die Zeilen- und Spaltennummer kennst. Das klingt nach dem nächsten Problem.

1
2
3
4
 UPDATE datei 
 SET city="ROM"
 WHERE name="Simpson"
 AND firstname="Lisa"; 

ist da der natürlichere Updateprozess. Ansonsten halten wir uns an rklm. ☺

Antworten |