%20%E2%80%93%20f%C3%BCr%20mehrere%20Dateien%20in%20BaSH.png)
Vorbehalt: Absoluter Anfänger. Ich muss einer CSV-Datei eine Spalte hinzufügen, deren Spaltenüberschrift „Name“ sein kann, die gesamte Spalte jedoch genau gleich sein sollte – der Name der Datei selbst, die filename
. Jede Datei hat jetzt nur 3 Variablen, aber 2100 Zeilen.
Beispiel: Für Datei"bcc1_45Fall_10010002.csv"das ist, was ich habe -
HUC8 YEAR RO_MM
10010002 1961 74.7
10010002 1962 69.1
10010002 1963 52.0
10010002 1964 130.7
10010002 1965 32.2
10010002 1966 85.4
Das ist was ich will -
NAME HUC8 YEAR RO_MM
bcc1_45Fall_10010002 10010002 1961 74.7
bcc1_45Fall_10010002 10010002 1962 69.1
bcc1_45Fall_10010002 10010002 1963 52.0
bcc1_45Fall_10010002 10010002 1964 130.7
bcc1_45Fall_10010002 10010002 1965 32.2
bcc1_45Fall_10010002 10010002 1966 85.4
Oder dieses -
HUC8 YEAR RO_MM
bcc1_45Fall_10010002 1961 74.7
bcc1_45Fall_10010002 1962 69.1
bcc1_45Fall_10010002 1963 52.0
bcc1_45Fall_10010002 1964 130.7
bcc1_45Fall_10010002 1965 32.2
bcc1_45Fall_10010002 1966 85.4
Wenn ich einfach alle Daten in der Spalte „HUC8“ durch diese ersetzen könnte, filename
wäre das perfekt. Es muss keine zusätzliche Spalte sein.
Ich muss dies für viele tausend Dateien tun.
Wenn ich wüsste, wie der erste Teil funktioniert, könnte ich eine Schleife erstellen. Aber vielleicht gibt es sogar einen besseren Weg?
Ich weiß nicht, wo ich anfangen soll.
Antwort1
Verwenden von awk
und column
:
$ awk '
NR==1{ sub(/\.csv$/, "", FILENAME) } # remove .csv suffix from FILENAME
NR>1{ $1=FILENAME } # replace the first field with filename
1 # print record
' bcc1_45Fall_10010002.csv | column -t
HUC8 YEAR RO_MM
bcc1_45Fall_10010002 1961 74.7
bcc1_45Fall_10010002 1962 69.1
bcc1_45Fall_10010002 1963 52.0
bcc1_45Fall_10010002 1964 130.7
bcc1_45Fall_10010002 1965 32.2
bcc1_45Fall_10010002 1966 85.4
Sie können dies in einer Shell-Schleife ausführen, um die geänderten Dateien im Verzeichnis zu speichern modified_files
:
mkdir modified_files &&
for i in *.csv; do
awk 'NR==1{ sub(/\.csv$/, "", FILENAME) } NR>1{ $1=FILENAME }1' "$i" |
column -t > "./modified_files/$i"
done
Wenn Sie eine Spalte ersetzen müssen HUC8
und dies nicht die erste Spalte ist, ändern Sie den Code wie folgt:
awk -v search='HUC8' '
NR==1{
for(i=1;i<=NF;i++)
if ($i==search){ fld=i; sub(/\.csv$/, "", FILENAME); break }
}
NR>1{ $fld=FILENAME }
1
' file.csv | column -t
Antwort2
Verwenden vonMüller, und vorausgesetzt, Ihre Dateien sind "einfache" CSV (keine KommasinnerhalbFelder usw. - Sie können --csvlite
zu ändern --csv
, wenn die volle RFC-4180-Unterstützung erforderlich ist)
$ cat bcc1_45Fall_10010002.csv
HUC8,YEAR,RO_MM
10010002,1961,74.7
10010002,1962,69.1
10010002,1963,52.0
10010002,1964,130.7
10010002,1965,32.2
10010002,1966,85.4
Dann
um die aktuelle
HUC8
Spalte zu ersetzen:$ mlr --csvlite put -S '$HUC8 = substr(FILENAME,0,-5)' bcc1_45Fall_10010002.csv HUC8,YEAR,RO_MM bcc1_45Fall_10010002,1961,74.7 bcc1_45Fall_10010002,1962,69.1 bcc1_45Fall_10010002,1963,52.0 bcc1_45Fall_10010002,1964,130.7 bcc1_45Fall_10010002,1965,32.2 bcc1_45Fall_10010002,1966,85.4
So fügen Sie eine separate
Name
Spalte hinzu:$ mlr --csvlite put -S '$Name = substr(FILENAME,0,-5)' bcc1_45Fall_10010002.csv HUC8,YEAR,RO_MM,Name 10010002,1961,74.7,bcc1_45Fall_10010002 10010002,1962,69.1,bcc1_45Fall_10010002 10010002,1963,52.0,bcc1_45Fall_10010002 10010002,1964,130.7,bcc1_45Fall_10010002 10010002,1965,32.2,bcc1_45Fall_10010002 10010002,1966,85.4,bcc1_45Fall_10010002
So fügen Sie eine
Name
Spalte als erste Spalte hinzu:$ mlr --csvlite put -S '$Name = substr(FILENAME,0,-5)' then reorder -f Name bcc1_45Fall_10010002.csv Name,HUC8,YEAR,RO_MM bcc1_45Fall_10010002,10010002,1961,74.7 bcc1_45Fall_10010002,10010002,1962,69.1 bcc1_45Fall_10010002,10010002,1963,52.0 bcc1_45Fall_10010002,10010002,1964,130.7 bcc1_45Fall_10010002,10010002,1965,32.2 bcc1_45Fall_10010002,10010002,1966,85.4
Alle oben genannten schreiben das Ergebnis in die Standardausgabe. Um die Datei direkt zu ändern, fügen Sie die -I
Option hinzu. Sie können mehrere Dateien gleichzeitig übergeben, indem Sie Shell-Globs verwenden, z. B. bcc*.csv
oder *.csv
.
[Beim Testenohne -I
die Kopfzeile wird nicht wiederholt, es sei denn, es ist aufgrund von Datensatzheterogenität eine neue Kopfzeile erforderlich; in jedem Fall -I
wird jeder Datei eine entsprechende Kopfzeile hinzugefügt.]
Antwort3
$ perl -lne 'BEGIN {$fnr=1};
if ($fnr == 1) {
($fn = $ARGV) =~ s/\.[^.]+$//;
print "NAME,$_"
} else {
print "$fn,$_"
};
$fnr++;
if (eof) {$fnr=1}' *.csv
Dadurch wird der Dateiname (ohne die CSV-„Erweiterung“) als erstes Feld hinzugefügt und der Inhalt der CSV-Dateien auf stdout gedruckt.
Im Gegensatz zu awk
verfolgt perl
es nicht die Zeilenanzahl für jede einzelne Datei (es verfolgt nur die Gesamtzeilenanzahl mit der $.
Variablen). Dieses Skript verwaltet diese Anzahl manuell, indem es zuerst die Variable $fnr
im BEGIN-Block setzt, sie dann für jede gelesene Zeile erhöht und sie schließlich jedes Mal, wenn das Ende einer Datei erreicht ist, auf 1 zurücksetzt.
Dies lässt sich leicht ändern, indem der Dateiname als letztes Feld statt als erstes angehängt wird. Ändern Sie beispielsweise die beiden print
Anweisungen wie folgt:
print "$_,NAME"
and:
print "$_,$fn"
Wenn Sie das Dateinamenfeld nicht als erstes Feld, sondern an einer anderen Stelle in der Zeile einfügen müssen, können Sie splice
die Funktion von Perl verwenden.
Folgendes fügt beispielsweise den Dateinamen als drittes Feld ein (beachten Sie, dass die Array-Indizes in Perl bei 0 und nicht bei 1 beginnen, das dritte Feld ist also $F[2]
, nicht $F[3]
):
$ perl -F, -lne 'BEGIN {$fnr=1; $field_num=2};
if ($fnr == 1) {
($fn = $ARGV) =~ s/\.[^.]+$//;
splice @F, $field_num, 0, "NAME";
} else {
splice @F, $field_num, 0, $fn;
};
print join(",", @F);
$fnr++;
if (eof) {$fnr=1}' *.csv
Dies verwendet die Perl- -F
Option, um ein Komma als Feldtrennzeichen festzulegen. Dies aktiviert auch die Auto-Split-Funktion von Perl, um die Eingabezeile automatisch in ein Array namens aufzuteilen @F
(dies ähnelt dem Standardverhalten von awk, bei dem die Eingabezeile automatisch in $1, $2, $3 usw. aufgeteilt wird). Entweder wird die wörtliche Zeichenfolge „NAME“ oder der geänderte Dateiname in @F eingefügt, dann werden die Elemente des @F
Arrays gedruckt und durch Kommazeichen verbunden.
Wenn Sie den Inhalt der Dateien tatsächlich ändern möchten, verwenden Sie die -i
Option von Perl. Sie können optional eine Sicherungskopie der Originaldatei erstellen, indem Sie der -i
Option eine „Erweiterung“ hinzufügen, z. B. umbenennen filename.csv
in filename.csv.orig
mit -iorig
. Beispiel:
perl -iorig -lne '......' *.csv
oder
perl -iorig -F, -lne '......' *.csv
Antwort4
Führen Sie dann eine Schleife über die Dateinamen durch und drucken Sie die Spalten mit awk
for f in *.csv;
do
head -1 $f > out/$f
cat $f | awk -v FIN=${f%.csv} 'NR>1 {print FIN, $2, $3}' >> out/$f
done
HUC8 YEAR RO_MM
bcc1_45Fall_10010002 1961 74.7
(...)