Spalte hinzufügen, gesamte Spalte mit Dateinamen füllen (ohne „.csv“) – für mehrere Dateien in BaSH

Spalte hinzufügen, gesamte Spalte mit Dateinamen füllen (ohne „.csv“) – für mehrere Dateien in BaSH

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, filenamewä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 awkund 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 HUC8und 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 --csvlitezu ä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

  1. um die aktuelle HUC8Spalte 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
    
  2. So fügen Sie eine separate NameSpalte 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
    
  3. So fügen Sie eine NameSpalte 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 -IOption hinzu. Sie können mehrere Dateien gleichzeitig übergeben, indem Sie Shell-Globs verwenden, z. B. bcc*.csvoder *.csv.

[Beim Testenohne -Idie Kopfzeile wird nicht wiederholt, es sei denn, es ist aufgrund von Datensatzheterogenität eine neue Kopfzeile erforderlich; in jedem Fall -Iwird 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 awkverfolgt perles 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 $fnrim 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 printAnweisungen 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 splicedie 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- -FOption, 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 @FArrays gedruckt und durch Kommazeichen verbunden.

Wenn Sie den Inhalt der Dateien tatsächlich ändern möchten, verwenden Sie die -iOption von Perl. Sie können optional eine Sicherungskopie der Originaldatei erstellen, indem Sie der -iOption eine „Erweiterung“ hinzufügen, z. B. umbenennen filename.csvin filename.csv.origmit -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 
(...)

verwandte Informationen