
Ich habe eine Reihe von Dateien, die jeweils ein bestimmtes Muster in ihren Namen enthalten, beispielsweise ABC1234001
solche, die Informationen über bestimmte Gruppen meiner Daten enthalten (Tabellen mit mehreren Spalten). Ich habe auch eine Tabelle info.tsv
wie 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.tsv
Datei. 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.tsv
verkettetgroup2.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 awk
ist 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, *.tsv
auf 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.tsv
in die Variablen group
, first
und last
.
Für jede auf diese Weise gelesene Zeile info.tsv
wird 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 true
und false
hier als Befehle und nicht als einfache Zeichenfolgen verwendet werden. Der in der Variablen gespeicherte Wert $collect
wird in ausgeführt, if ! "$collect"
d. h. das Skript führt einen der beiden in der Shell integrierten Befehle true
oder false
aus. 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 set
Befehls 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 filename
Variable 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 ABC
Dateien heißen (solange sie eine .tsv
Dateinamenerweiterung 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
ABC
Dadurch werden zunächst alle Instanzen von aus der Datei entfernt, info.tsv
sodass wir nur die Zahlen erhalten. Beachten Sie, dass dies die genaue Datenstruktur voraussetzt, die Sie uns gezeigt haben. Wenn ABC
auch im Gruppennamen vorhanden sein kann, wird dies unterbrochen.
Nach dem Entfernen ABC
wird das Ergebnis in die Schleife weitergeleitet while
, die drei Variablen liest: $group
, $start
und $end
. Diese werden dann an übergeben, eval
das 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 eval
wird im $files
Array 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.