
Dies ist meine Eingabedatei
0164318,001449,001452,001922
0164318,001456,001457,001922
0842179,002115,002118,001485
0846354,001512,001513,001590
0841422,001221,001224,001860
0841422,001227,001228,001860
Ich möchte mein Ergebnis als
0164318,001449,001457,001922
0842179,002115,002118,001485
0846354,001512,001513,001590
0841422,001221,001228,001860
Gruppieren Sie mithilfe von Spalte1 und finden Sie Min. (Spalte2) und Max. (Spalte3)
über ein Shell-Skript.
Antwort1
Verwenden voncsvkit
,
$ csvsql -H --query 'SELECT a,min(b),max(c),d FROM file GROUP BY a' file.csv
a,min(b),max(c),d
164318,1449,1457,1922
841422,1221,1228,1860
842179,2115,2118,1485
846354,1512,1513,1590
Dadurch werden die CSV-Daten in eine temporäre Datenbank geladen (standardmäßig SQLite, glaube ich) und dann die angegebene SQL-Abfrage darauf angewendet. Die Tabelle hat standardmäßig denselben Namen wie die Eingabedatei (ohne Suffix) und da die Daten keine Spaltenüberschriften haben, sind die Standardfeldnamen alphabetisch.
Die -H
Optionen geben an csvsql
, dass die Daten keine Spaltenüberschriften haben.
Um den generierten Header in der Ausgabe zu löschen, leiten Sie das Ergebnis durch etwas wie weiter sed '1d'
.
So erhalten Sie mit Nullen aufgefüllte Ganzzahlen:
$ csvsql -H --query 'SELECT printf("%07d,%06d,%06d,%06d",a,min(b),max(c),d) FROM file GROUP BY a' file.csv
"printf(""%07d,%06d,%06d,%06d"",a,min(b),max(c),d)"
"0164318,001449,001457,001922"
"0841422,001221,001228,001860"
"0842179,002115,002118,001485"
"0846354,001512,001513,001590"
Hier werden die Zeilen in Anführungszeichen gesetzt, da wir eigentlich nur ein einziges Ausgabefeld für jeden Ergebnisdatensatz anfordern (und dieses enthält Kommas). Eine andere Möglichkeit, dies zu tun, erfordert zwar etwas mehr Tipparbeit, erzeugt aber keine zusätzlichen Anführungszeichen:
$ csvsql -H --query 'SELECT printf("%07d",a),printf("%06d",min(b)),printf("%06d",max(c)),printf("%06d",d) FROM file GROUP BY a' file.csv
"printf(""%07d"",a)","printf(""%06d"",min(b))","printf(""%06d"",max(c))","printf(""%06d"",d)"
0164318,001449,001457,001922
0841422,001221,001228,001860
0842179,002115,002118,001485
0846354,001512,001513,001590
Auch hier kann der Ausgabeheader entfernt werden, indem das Ergebnis weitergereicht wird sed '1d'
.
Antwort2
Verwenden voncsvkit
:
csvsql -H --query "select a,min(b),max(c),d from file group by a,d" file.csv
Beachten Sie, dass dadurch die führende 0 abgeschnitten wird.
Ausgabe:
a,min(b),max(c),d
164318,1449,1457,1922
841422,1221,1228,1860
842179,2115,2118,1485
846354,1512,1513,1590
Antwort3
Mit Miller (http://johnkerl.org/miller/doc), mit
mlr --ocsv --quote-all --inidx --ifs , cat inputFile | \
mlr --ocsv --quote-none --icsvlite stats1 -g '"1"' -a min,max,min -f '"2","3","4"' \
then cut -f '"1","2"_min,"3"_max,"4"_min' \
then label id,col2,col3,col4 | sed 's/"//g'
du hast
id,col2,col3,col4
0164318,001449,001457,001922
0842179,002115,002118,001485
0846354,001512,001513,001590
0841422,001221,001228,001860
Antwort4
Sie können Ihr SQL in grundlegende prozedurale Operationen aufteilen und diese in einem Shell-Skript replizieren.
Dies ist natürlich keine gute Idee, da einer der Vorteile deklarativer Sprachen (wie SQL) darin besteht, dass sie die Ausführlichkeit und Komplexität der prozeduralen Implementierung vor den Entwicklern verbergen, sodass diese sich auf die Daten konzentrieren können. (Optimierung ist ein zweiter großer Vorteil deklarativer Sprachen, der verloren geht, wenn Sie sie mit einem prozeduralen Programm replizieren.)
Außerdem ist dieser Ansatz problematisch, weilDie Verarbeitung von Text in Shell-Schleifen wird normalerweise als schlechte Praxis angesehen.
Hier ist jedoch ein Beispiel für ein Shell-Skript, das Standarddienstprogramme nutzt, die auf vielen Systemen vorinstalliert sind (mit Ausnahme der Array-Konstruktion – nicht in POSIX spezifiziert, aber weit verbreitet und sicherlich auch für Sie verfügbar, da Sie danach fragen bash
):
#!/bin/bash
# The input file will be passed as the first argument
file="$1"
# For each input line:
# We take only the values of the first field, sort them, remove duplicates
for i in $(cut -d ',' -f 1 "$file" | sort -n -u); do
# Resetting the array is not really needed; we do it for safety
out=()
# The first field of the output row is the key of the loop
out[0]="$i"
# We only consider the rows whose first field is equal
# to the current key (grep) and...
# ... we sort the values of the second field
# in ascending order and take only the first one
out[1]="$(grep "^${out[0]}" "$file" | cut -d ',' -f 2 | sort -n | head -n 1)"
# ... we sort the values of the third field in
# ascending order and take only the last one
out[2]="$(grep "^${out[0]}" "$file" | cut -d ',' -f 3 | sort -n | tail -n 1)"
# ... we sort the values of the fourth field in
# ascending order and take only the first one
out[3]="$(grep "^${out[0]}" "$file" | cut -d ',' -f 4 | sort -n | head -n 1)"
# Finally we print out the output, separating fields with ','
printf '%s,%s,%s,%s\n' "${out[@]}"
done
Es soll aufgerufen werden als
./script file
Dieses Skript entspricht
SELECT col1, MIN(col2), MAX(col3), MIN(col4)
FROM text
GROUP BY col1
ORDER BY col1