agregar columna, completar toda la columna con el nombre del archivo (sin ".csv") - para múltiples archivos en BaSH

agregar columna, completar toda la columna con el nombre del archivo (sin ".csv") - para múltiples archivos en BaSH

Advertencia: principiante absoluto. Necesito agregar una columna a un archivo .csv donde el encabezado de la columna pueda ser "Nombre", pero toda la columna debe ser exactamente igual: el nombre del archivo en sí, el archivo filename. Cada archivo tiene ahora sólo 3 variables, pero 2100 filas.

Ejemplo: para archivo"bcc1_45Fall_10010002.csv"Esto es lo que tengo -

   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

Esto es lo que quiero -

  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

O esto -

  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

Si pudiera simplemente reemplazar todos los datos en la columna "HUC8" con eso, filenamesería perfecto. No es necesario que sea una columna adicional.

Necesito hacer esto para muchos miles de archivos.

Si supiera cómo hacer la primera parte, podría crear un bucle. ¿Pero tal vez haya incluso una manera mejor?

No se donde empezar.

Respuesta1

Usando awky 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

Puede ejecutar esto en un bucle de shell para guardar los archivos modificados en el directorio 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

Si necesita reemplazar la columna HUC8y esta no es la primera columna, cambie el código a este:

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

Respuesta2

UsandoMolinero, y suponiendo que sus archivos sean CSV "simples" (sin comasdentrocampos, etc. (puede cambiar --csvlitea --csvsi se requiere compatibilidad total con RFC-4180)

$ 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

entonces

  1. para reemplazar la HUC8columna actual:

     $ 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. para agregar una Namecolumna separada:

     $ 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. para agregar una Namecolumna como primera columna:

     $ 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
    

Todo lo anterior escribe el resultado en la salida estándar; para modificar el archivo en el lugar, agregue la -Iopción. Puede pasar varios archivos a la vez usando Shell globs ex. bcc*.csvo *.csv.

[Al probarsin -Ila línea del encabezado no se repetirá a menos que se requiera un nuevo encabezado como resultado de la heterogeneidad del registro; sin embargo, -Ise agregará un encabezado apropiado a cada archivo.]

Respuesta3

$ perl -lne 'BEGIN {$fnr=1};

             if ($fnr == 1) {
               ($fn = $ARGV) =~ s/\.[^.]+$//;
               print "NAME,$_"
             } else {
               print "$fn,$_"
             };

             $fnr++;

             if (eof) {$fnr=1}' *.csv

Esto agregará el nombre del archivo (sin la "extensión" .csv) como primer campo e imprimirá el contenido de los archivos .csv en la salida estándar.

A diferencia de awk, perlno realiza un seguimiento del recuento de líneas de cada archivo individual (sólo realiza un seguimiento del recuento total de líneas, con la $.variable). Este script mantiene ese recuento manualmente, primero configurando la variable $fnren el bloque BEGIN, luego incrementándola para cada línea leída y finalmente restableciéndola a 1 cada vez que se llega al final de un archivo.

Esto se modifica fácilmente para agregar el nombre del archivo como último campo en lugar del primero. por ejemplo, cambie las dos printdeclaraciones a:

      print "$_,NAME"
and: 
      print "$_,$fn"

Si necesita insertar el campo de nombre de archivo en algún otro lugar de la línea, en lugar de como el primer campo, puede usar splicela función de Perl.

Por ejemplo, lo siguiente inserta el nombre del archivo como tercer campo (tenga en cuenta que los índices de la matriz Perl comienzan desde cero, no desde 1, por lo que el tercer campo es $F[2], no $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

Esto utiliza la opción de Perl -Fpara establecer una coma como delimitador de campo. Esto también permite que la función de división automática de Perl divida automáticamente la línea de entrada en una matriz llamada @F(esto es similar al comportamiento predeterminado de awk de dividir automáticamente la línea de entrada en $1, $2, $3, etc.). La cadena literal "NOMBRE" o el nombre de archivo modificado se empalma en @F, luego los elementos de la @Fmatriz se imprimen, unidos con caracteres de coma.

Finalmente, si realmente desea cambiar el contenido de los archivos, use -ila opción de Perl. Opcionalmente, puede mantener una copia de seguridad del archivo original utilizando una "extensión" con la -iopción, por ejemplo, cambiar el nombre filename.csva . Por ejemplo:filename.csv.orig-iorig

perl -iorig -lne '......' *.csv

o

perl -iorig -F, -lne '......' *.csv

Respuesta4

Luego recorra los nombres de archivos e imprima columnas con 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 
(...)

información relacionada