Hier ist eine einfache Zeile meiner Verwendung:
grep -i '"location_country":"country name"' file.txt >> sample.txt
Ich durchsuche eine große Datei mit mehreren Ländern und möchte dynamisch eine Textdatei mit dem Ländernamen erstellen und alle Übereinstimmungen aus demselben Land in dieser Datei speichern, also country name.txt
für jedes Vorkommen.
etwas wie das
grep -i '"location_country":"(.+)"' file.txt >> \1.txt
Beispieldaten:
{"full_name":"name1","location_country":"united kingdom"}
{"full_name":"name2","location_country":"united states"}
{"full_name":"name3","location_country":"china"}
Ich muss also drei separate Textdateien mit Ländernamen erstellen, die beispielsweise Folgendes united kingdom.txt
enthalten:
{"full_name":"name1","location_country":"united kingdom"}
Ich verwende bereits Bash-Skripte, daher stört mich das nicht. Wie könnte ich das erreichen? Ich verwende eine Linux-Maschine.
Antwort1
Ihre Datei besteht aus einer Reihe von JSON-Objekten. Jedes Objekt enthält einen .location_country
Schlüssel. Aus jedem Objekt können wir einen Shell-Befehl erstellen, der eine serialisierte Kopie des Objekts selbst in eine Datei schreibt, die nach dem Wert des .location_country
Schlüssels benannt ist. Diese Shell-Befehle könnten dann von der Shell ausgeführt werden.
Verwenden von jq
,
jq -r '"printf \"%s\\n\" \(. | @json | @sh) >\(.location_country|@sh).txt"' file.txt
@json
Das serialisierte Objekt kann mit dem Operator in erstellt werden jq
, der einen JSON-codierten String ausgibt, der das Eingabedokument enthält, in diesem Fall das aktuelle Objekt. Dieser wird dann an weitergeleitet, @sh
um den String für die Shell richtig in Anführungszeichen zu setzen. Der @sh
Operator wird auch verwendet, um einen Teil des Ausgabedateinamens aus dem Wert des .location_country
Schlüssels zu erstellen.
Der Befehl erstellt im Wesentlichen Shellcode, der aufruft printf
, das aktuelle Objekt ausgibt und die Ausgabe in eine bestimmte Datei umleitet.
Ausgehend von Ihren Beispieldaten in file.txt
würde dies Folgendes ausgeben:
printf "%s\n" '{"full_name":"name1","location_country":"united kingdom"}' >'united kingdom'.txt
printf "%s\n" '{"full_name":"name2","location_country":"united states"}' >'united states'.txt
printf "%s\n" '{"full_name":"name3","location_country":"china"}' >'china'.txt
Sie können dies in eine separate Datei umleiten und es ausführen, um sh
die Befehle auszuführen, oder Sie können Folgendes eval
direkt in der Shell verwenden:
eval "$( jq ...as above... )"
Da wir einen richtigen JSON-Parser verwenden, jq
würde das oben genannte auch dann funktionieren, wenn das JSON-Eingabedokument nicht mit einem einzelnen Objekt pro Zeile formatiert ist.
$ cat file.txt
{
"full_name": "name1",
"location_country": "united kingdom"
}
{
"full_name": "name2",
"location_country": "united states"
}
{
"full_name": "name3",
"location_country": "china"
}
$ jq -r '"printf \"%s\\n\" \(. | @json | @sh) >\(.location_country|@sh).txt"' file.txt
printf "%s\n" '{"full_name":"name1","location_country":"united kingdom"}' >'united kingdom'.txt
printf "%s\n" '{"full_name":"name2","location_country":"united states"}' >'united states'.txt
printf "%s\n" '{"full_name":"name3","location_country":"china"}' >'china'.txt
$ eval "$( jq -r '"printf \"%s\\n\" \(. | @json | @sh) >\(.location_country|@sh).txt"' file.txt )"
$ ls
china.txt file.txt united kingdom.txt united states.txt
$ cat 'united kingdom.txt'
{"full_name":"name1","location_country":"united kingdom"}
Antwort2
Verwenden vonawk
Eingang
$ cat input_file
{"full_name":"name1","location_country":"united kingdom"}
{"full_name":"name2","location_country":"united states"}
{"full_name":"name3","location_country":"china"}
{"full name":"name12","location":"china"}
{"full name":"name11","location":"china"}
awk -F"[\"|:]" '$10~/[A-Za-z]/ {print > $10".txt"}' input_file
Ausgabe
$ cat china.txt
{"full_name":"name3","location_country":"china"}
{"full name":"name12","location":"china"}
{"full name":"name11","location":"china"}
$ cat united\ kingdom.txt
{"full_name":"name1","location_country":"united kingdom"}
$ cat united\ states.txt
{"full_name":"name2","location_country":"united states"}
Antwort3
In Anbetracht Ihrer Kommentare weiter unten sollte dies das gewünschte Ergebnis erzielen, indem GNU awk für das 3. Argument für match() und die Verarbeitung vieler gleichzeitig geöffneter Dateien verwendet wird:
awk 'match($0,/"location_country":"([^"]+)"/,a) { print > (a[1] ".txt") }' file
Aus Gründen der Ausführungsgeschwindigkeit wäre jedoch wahrscheinlich ein Ansatz vom Typ „Dekorieren/Sortieren/Verwenden/Aufheben der Dekoration“ am besten, z. B.:
awk -v OFS='"' 'match($0,/"location_country":"[^"]+"/) { print substr($0,RSTART+20,RLENGTH-21), $0 }' file |
sort -t'"' -k1,1 |
awk -F'"' '$1!=prev { close(out); out=$1 ".txt"; prev=$1 } { print > out }' |
cut -d'"' -f2-
und das funktioniert mit jedem Sort-, Awk- und Cut-Verfahren.
Ursprüngliche Antwort:
Wenn Ihre Daten immer so einfach/regelmäßig sind, benötigen Sie mit GNU awk nur Folgendes (um viele gleichzeitig geöffnete Ausgabedateien zu verarbeiten):
awk -F'"' '{ print > ($5 ".txt") }' file
oder dies mit jedem awk:
awk -F'"' '{
out = $5 ".txt"
if ( !seen[out]++ ) {
printf "" > out
}
print >> out
close(out)
}' file
Das Obige funktioniert unabhängig von der Größe Ihrer Eingabedatei, solange Sie über freien Speicherplatz zum Erstellen der Ausgabedateien verfügen.
Sie können es effizienter machen, indem Sie zuerst nach dem Ländernamen sortieren, wenn Sie das möchten:
sort -t'"' -k5,5 file |
awk -F'"' '$5 != prev{ close(out); out=$5 ".txt"; prev=$5 } { print > out }'
Das letzte Skript funktioniert mit jedem Sort und jedem Awk, aber es könnte die Reihenfolge der Eingabezeilen für jedes Land neu anordnen. Wenn Ihnen das wichtig ist und Sie GNU Sort haben, fügen Sie das -s
Argument hinzu. Wenn Ihnen das wichtig ist und Sie GNU Sort nicht haben, lassen Sie es mich wissen, da es einen ziemlich einfachen Workaround gibt.