Verketten Sie Dateien nach Tabellenwerten

Verketten Sie Dateien nach Tabellenwerten

Ich habe eine Reihe von Dateien, die jeweils ein bestimmtes Muster in ihren Namen enthalten, beispielsweise ABC1234001solche, die Informationen über bestimmte Gruppen meiner Daten enthalten (Tabellen mit mehreren Spalten). Ich habe auch eine Tabelle info.tsvwie diese:

group1    ABC1234001    ABC1234010
group2    ABC1234011    ABC1234018
group3    ABC1234019    ABC1234028
...       ...           ...

Es beinhaltet:

  • Spalte „Gruppe“, die die Gruppe angibt,
  • Spalte „erste Datei“, die das Muster für die erste Datei (alphabetische Reihenfolge) angibt, die Informationen für die entsprechende Gruppe enthält,
  • Spalte „letzte Datei“, die das Muster für die letzte Datei (alphabetische Reihenfolge) angibt, die Informationen für die entsprechende Gruppe enthält.

Ich muss also die Dateien für jede Gruppe in einer Datei zusammenfassen - genau wie

cat ABC123401{1..8}* >> group2.tsv

wäre für group2 als Beispiel - beim Lesen dieser info.tsvDatei. In diesem gegebenen Beispiel werden alle Dateien ( ABC1234011.tsv, ABC1234012.tsv, ABC1234013.tsv, ABC1234014.tsv, ABC1234015.tsv, ABC1234016.tsv, ABC1234017.tsv, ) zu einer Datei ABC1234018.tsvverkettetgroup2.tsv

Ich werde Folgendes tun:

while read $file; do
  #assign columns to variables like $1="group", $2="firstfile", $3="lastfile"
  cat *{$2..$3}* > $1.tsv;
done < info.tsv

Ich bin mir jedoch nicht ganz sicher, wie ich bei diesem Ansatz Variablen iterativ ändern kann. Vielleicht awkist die Verwendung von sinnvoller, aber ich weiß es nicht. Das Skript sollte eine Reihe von Dateien mit dem Namen group1.tsv, , erzeugen group2.tsv, die den Inhalt der entsprechenden Dateien von „erster Datei“ bis „letzter Datei“ in der Tabelle enthalten. Bitte helfen Sie mir, das entsprechende Skript zu schreiben.

Antwort1

Das folgende Skript geht davon aus, dass alle Dateien, die Sie verketten möchten, dem Muster entsprechen *.tsv. Wenn Sie wissen, dass sie alle übereinstimmen ABC*.tsv, können Sie dieses Muster am Anfang des Skripts anstelle von verwenden *.tsv.

Das Skript geht außerdem davon aus, dass alle Namen von Dateien, die in eine bestimmte Gruppe gehören, als fortlaufende Unterliste der Liste generiert werden, *.tsvauf die sie erweitert werden.

#!/bin/sh

set -- *.tsv

while read -r group first last; do
        collect=false

        for name do
                if ! "$collect"; then
                        [ "$name" = "$first.tsv" ] || continue
                        collect=true
                fi

                if "$collect"; then
                        cat -- "$name"
                        [ "$name" = "$last.tsv" ] && break
                fi
        done >"$group.tsv"

done <info.tsv

Das Skript setzt die Liste der Positionsparameter auf die Liste der Namen, die mit übereinstimmen *.tsv. Anschließend liest es die drei Felder jeder Zeile aus info.tsvin die Variablen group, firstund last.

Für jede auf diese Weise gelesene Zeile info.tsvwird die Liste der Positionsparameter nach Namen durchsucht, die dem ersten Namen in der Gruppe entsprechen. Sobald dieser erste Name gefunden ist, setzen wir ein Flag, collect, das der Logik des Skripts mitteilt, ab der aktuellen Position in der Liste mit dem Sammeln der Daten aus den in der Liste der Positionsparameter genannten Dateien zu beginnen. Dies endet, sobald wir auf einen Namen stoßen, der dem letzten Namen einer Gruppe entspricht.

Beachten Sie, dass trueund falsehier als Befehle und nicht als einfache Zeichenfolgen verwendet werden. Der in der Variablen gespeicherte Wert $collectwird in ausgeführt, if ! "$collect"d. h. das Skript führt einen der beiden in der Shell integrierten Befehle trueoder falseaus. Die Shell hat keine speziellen Schlüsselwörter für true oder false wie einige andere Sprachen (z. B. Python).

Testen:

$ ls
script
$ touch ABC{1234001..1234030}.tsv
$ for name in ABC*.tsv; do printf 'Name: %s\n' "$name" >"$name"; done
$ cat ABC1234015.tsv
Name: ABC1234015.tsv
$ cat >info.tsv <<END_DATA
group1 ABC1234001 ABC1234010
group2 ABC1234025 ABC1234030
END_DATA
$ ./script
$ cat group1.tsv
Name: ABC1234001.tsv
Name: ABC1234002.tsv
Name: ABC1234003.tsv
Name: ABC1234004.tsv
Name: ABC1234005.tsv
Name: ABC1234006.tsv
Name: ABC1234007.tsv
Name: ABC1234008.tsv
Name: ABC1234009.tsv
Name: ABC1234010.tsv
$ cat group2.tsv
Name: ABC1234025.tsv
Name: ABC1234026.tsv
Name: ABC1234027.tsv
Name: ABC1234028.tsv
Name: ABC1234029.tsv
Name: ABC1234030.tsv

Wie in den Kommentaren zu dieser Antwort erwähnt, würde ich dieses Skript für meinen persönlichen Gebrauch so weiterentwickeln, dass es folgendermaßen aussieht:

#!/bin/sh

while read -r group first last; do
        collect=false

        for name do
                filename=$( basename "$name" )

                if ! "$collect"; then
                        [ "$filename" = "$first.tsv" ] || continue
                        collect=true
                fi

                if "$collect"; then
                        cat -- "$name"
                        [ "$filename" = "$last.tsv" ] && break
                fi
        done >"$group.tsv"

done

Beachten Sie die Löschung des setBefehls oben (dieser wird durch Befehlszeilenargumente ersetzt) ​​und die Löschung der Umleitung von info.tsv(diese wird durch eine Umleitung auf der Befehlszeile ersetzt). Ich habe auch eine filenameVariable eingeführt, die die Dateinamenkomponente der auf der Befehlszeile angegebenen Pfadnamen enthält.

Ich würde das Skript dann folgendermaßen ausführen:

$ ./script ABC*.tsv <info.tsv

Damit habe ich ein Skript erstellt, dem es egal ist, wo die Eingabegruppenliste gespeichert ist und wie sie heißt. Dem ist es egal, wie die ABCDateien heißen (solange sie eine .tsvDateinamenerweiterung haben) und wo sie gespeichert sind.

Antwort2

Ihr Ansatz ist eine gute Idee, funktioniert aber leider nicht, da Variablen innerhalb von Klammererweiterungen nicht erweitert werden:

$ echo {1..5}
1 2 3 4 5
$ a=1
$ b=5
$ echo {$a..$b}
{1..5}

Sie können dies jedoch umgehen, indem Sie Folgendes verwenden eval:

sed 's/ABC//g' info.tsv | 
    while read -r group start end; do 
        files=( $(eval echo ABC{$start..$end}.tsv) )
        cat "${files[@]}" > "$group.tsv"; 
    done 

ABCDadurch werden zunächst alle Instanzen von aus der Datei entfernt, info.tsvsodass wir nur die Zahlen erhalten. Beachten Sie, dass dies die genaue Datenstruktur voraussetzt, die Sie uns gezeigt haben. Wenn ABCauch im Gruppennamen vorhanden sein kann, wird dies unterbrochen.

Nach dem Entfernen ABCwird das Ergebnis in die Schleife weitergeleitet while, die drei Variablen liest: $group, $startund $end. Diese werden dann an übergeben, evaldas die Variable erweitert, bevor die Klammererweiterung aufgerufen wird, sodass Sie eine Liste mit Dateinamen erhalten:

$ eval echo ABC{1..5}
ABC1 ABC2 ABC3 ABC4 ABC5

Das Ergebnis evalwird im $filesArray gespeichert, das als Eingabe an Folgendes übergeben wird cat:

cat "${files[@]}" > "$group.tsv";

Antwort3

Wenn ich Sie richtig verstehe, hier ist eine Option

$ while IFS= read -r i; do
    f=$(echo "$i" | cut -d' ' -f1)
    cat $(echo "$i" | cut -d' ' -f2- | sed -E 's/([0-9])\s+/\1.tsv /;s/([0-9])$/\1.tsv /') > "$f.txt"
  done < info.tsv

  • f=$(echo "$i" | cut -d' ' -f1)ruft den Namen der Gruppe ab.
  • cat $(cut -d' ' -f2- | sed -E 's/([0-9])\s+|([0-9])$/\1.tsv /g')verkettet die Liste der Dateien in der Zeile.

verwandte Informationen