Parsen der Ausgabe mit dynamischen Spaltenbreiten und leeren Feldern

Parsen der Ausgabe mit dynamischen Spaltenbreiten und leeren Feldern

gdriveverfügt über einen Unterbefehl list, der eine Liste von Dateien wie im folgenden Beispiel druckt:

gdrive list

Ausgabe:

Id                                  Name                      Type   Size     Created
1sV3_a1ySV0-jbLxhA8NIEts1KU_aWa-5   info.pdf                  bin    10.0 B   2018-08-27 20:26:20
1h-j3B5OLryp6HkeyTsd9PJaAtKK_GYyl   2018-12-ss-scalettapass   dir             2018-08-27 20:26:19

Ich versuche, diese Ausgabe mit Tools wie awkund zu analysieren, sedohne Erfolg.

Die Probleme sind leere „Felder“ in der Größenspalte und die dynamische Breite der Spalten.

Hat jemand eine Idee, wie diese Ausgabe analysiert werden kann?

Antwort1

awk kann mit Daten fester Breite umgehen. Zuerst müssen wir die Spaltenbreiten bestimmen:

fieldwidths=$(head -n 1 file | grep -Po '\S+\s*' | awk '{printf "%d ", length($0)}')

Dieser Wert ist "36 26 7 9 7 "– das letzte Feld ist länger als 7 Zeichen. Lassen Sie uns daraus beliebig 70 Zeichen machen:

fieldwidths=${fieldwidths/% /0}

Lassen Sie uns nun die Daten lesen und in CSV umwandeln:

awk -v FIELDWIDTHS="$fieldwidths" '{
    for (i=1; i<=NF; i++) {
        val = $i
        sub(/ *$/, "", val)
        gsub(/"/, "\"\"", val)
        printf "%s\"%s\"", (i==1 ? "" : ","), val
    }
    print ""
}' file

Ausgänge:

"Id","Name","Type","Size","Created"
"1sV3_a1ySV0-jbLxhA8NIEts1KU_aWa-5","info.pdf","bin","10.0 B","2018-08-27 20:26:20"
"1h-j3B5OLryp6HkeyTsd9PJaAtKK_GYyl","2018-12-ss-scalettapass","dir","","2018-08-27 20:26:19"

Dieselbe Funktionalität mit Perl

perl -lne '
    if ($. == 1) {
        @head = ( /(\S+\s*)/g );
        pop @head;
        $patt = "^";
        $patt .= "(.{" . length($_) . "})" for @head;
        $patt .= "(.*)\$";
    }
    print join ",", map {s/"/""/g; s/\s+$//; qq("$_")} (/$patt/o);
' file

Antwort2

Dies gelingt Ihnen mit Perlder unpackFunktion, indem Sie die Entpackvorlage dynamisch durch Untersuchung des Headers (1. Zeile) erstellen:

perl -lpe '
    $fmt //= join "", map("A" . length(), /\H+\h+(?=\H)/g), "A*";
    $_ = join ",", map { s/"/""/gr =~ s/(.*)/"$1"/r } unpack $fmt;
' input-file.txt

Erläuterung:

  • -pwird perldie Datei zeilenweise nutzen. Jede Zeile, auch Datensatz genannt, wird als bezeichnet $_. Ein weiterer Effekt von -pist, dass der aktuelle Datensatz automatisch gedruckt wird, bevor der nächste abgerufen wird.
  • -lmacht 2 Dinge, setztORS = RS = \n
  • Der reguläre Ausdruck /\H+\h+(?=\H)/gsoll alle Felder außer dem letzten abrufen und diese dann an weiterleiten map.
  • mapberechnet die Längen dieser Felder und stellt jedem ein „A“ voran.
  • Anstatt das letzte Feld oben nicht auszuwählen, fügen wir einen Sammelbegriff „A*“ hinzu.
  • Diese werden dann an übergeben, joindas sie mit dem Null-Trennzeichen zu einem String zusammenfügt. Damit ist das Entpackformat einsatzbereit und muss aufgrund des //=Operators, der die defined-orFunktion darstellt, nicht erneut berechnet werden.
  • Nun können wir, ausgerüstet mit dem dynamisch erstellten Entpackformat, mit der Anwendung auf jede Zeile fortfahren, einschließlich der Kopfzeile.
  • unpackentpackt einen String, in unserem Fall die aktuelle Zeile, im bereitgestellten Format und gibt entpackte Felder aus.
  • Diese ausgegebenen Felder werden dann als Eingabe verwendet. Das mapProgramm bearbeitet sie nacheinander und führt die im { ... }Code beschriebenen Schritte aus. In unserem Fall machen wir in jedem Feld Folgendes: a) Verdoppeln Sie die Anführungszeichen. b) Versehen Sie das Feld mit Anführungszeichen.
  • Nachdem mapdie Bearbeitung der Felder abgeschlossen ist, werden sie an übergeben join, das sie mithilfe von Kommas ,zu einer hübschen kleinen CSVDatei zusammenfügt.
  • PS:Beachten Sie, dass wir die nachstehenden Leerzeichen in den von generierten Feldern nicht entfernen mussten unpack, da unpackdies bei Verwendung des AFormatierungszeichens (A für ASCII) für Sie erledigt wird.

Ausgabe:

"Id","Name","Type","Size","Created"
"1sV3_a1ySV0-jbLxhA8NIEts1KU_aWa-5","info.pdf","bin","10.0 B","2018-08-27 20:26:20"
"1h-j3B5OLryp6HkeyTsd9PJaAtKK_GYyl","2018-12-ss-scalettapass","dir","","2018-08-27 20:26:19"

Dies kann vom sedTool durchgeführt werden, erfordert aber einen zweistufigen Ansatz. Dabei generieren wir zunächst mithilfe der Kopfzeile der Eingabe seddynamisch ein Skript, das dann die Eingabedatei (einschließlich der Kopfzeile) verarbeitet, um die gewünschte Operation auszuführen, wie gezeigt:

if="input-file.txt"
cmd=$(< "$if" head -n 1 | perl -lne 'print join $/, reverse map { $s += length();qq[s/./\\n/$s] } /\H+\h+(?=\H)/g')
sed -e '
    '"${cmd}"'
    s/"/""/g
    s/[[:blank:]]*\n/","/g
    s/.*/"&"/
' < "$if"

verwandte Informationen