Nach Muster suchen und Datei mit gleichem Namen erstellen

Nach Muster suchen und Datei mit gleichem Namen erstellen

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.txtfü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.txtenthalten:

{"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_countrySchlü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_countrySchlü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

@jsonDas 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, @shum den String für die Shell richtig in Anführungszeichen zu setzen. Der @shOperator wird auch verwendet, um einen Teil des Ausgabedateinamens aus dem Wert des .location_countrySchlü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.txtwü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 shdie Befehle auszuführen, oder Sie können Folgendes evaldirekt in der Shell verwenden:

eval "$( jq ...as above... )"

Da wir einen richtigen JSON-Parser verwenden, jqwü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 -sArgument hinzu. Wenn Ihnen das wichtig ist und Sie GNU Sort nicht haben, lassen Sie es mich wissen, da es einen ziemlich einfachen Workaround gibt.

verwandte Informationen