adicione coluna, preencha a coluna inteira com o nome do arquivo (sem o ".csv") - para vários arquivos no BaSH

adicione coluna, preencha a coluna inteira com o nome do arquivo (sem o ".csv") - para vários arquivos no BaSH

Advertência: Iniciante absoluto. Preciso adicionar uma coluna a um arquivo .csv onde o cabeçalho da coluna pode ser "Nome", mas a coluna inteira deve ser exatamente a mesma - o nome do arquivo em si, a extensão filename. Cada arquivo tem apenas 3 variáveis ​​agora, mas 2.100 linhas.

Exemplo: Para arquivo"bcc1_45Fall_10010002.csv"isso é o que eu tenho -

   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

É isso que eu quero -

  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

Ou isto -

  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

Se eu pudesse simplesmente substituir todos os dados da coluna "HUC8" por isso filenameseria perfeito. Não precisa ser uma coluna extra.

Preciso fazer isso para muitos milhares de arquivos.

Se eu soubesse fazer a primeira parte, poderia criar um loop. Mas talvez haja uma maneira ainda melhor?

Não sei por onde começar.

Responder1

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

Você pode executar isso em um loop de shell para salvar os arquivos modificados no diretório 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

Se você precisar substituir a coluna HUC8e esta não for a primeira coluna, altere o código para isto:

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

Responder2

UsandoMoleiro, e assumindo que seus arquivos sejam CSV "simples" (sem vírgulasdentro decampos e assim por diante - você pode alterar --csvlitepara --csvse for necessário suporte completo a 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

então

  1. para substituir a HUC8coluna atual:

     $ 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 adicionar uma Namecoluna 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 adicionar uma Namecoluna como a primeira coluna:

     $ 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
    

Todos os itens acima gravam o resultado na saída padrão - para modificar o arquivo no local, adicione a -Iopção. Você pode passar vários arquivos de uma vez usando shell globs ex. bcc*.csvou *.csv.

[Ao testarsem -Ia linha do cabeçalho não será repetida, a menos que um novo cabeçalho seja necessário como resultado da heterogeneidade do registro; no entanto, -Ium cabeçalho apropriado será adicionado a cada arquivo.]

Responder3

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

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

             $fnr++;

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

Isso adicionará o nome do arquivo (sem a "extensão" .csv) como o primeiro campo e imprimirá o conteúdo dos arquivos .csv em stdout.

Ao contrário do awk, perlnão rastreia a contagem de linhas de cada arquivo individual (ele rastreia apenas a contagem total de linhas, com a $.variável). Este script mantém essa contagem manualmente, primeiro definindo a variável $fnrno bloco BEGIN, depois incrementando-a para cada linha lida e, finalmente, redefinindo-a para 1 sempre que o final de um arquivo é atingido.

Isso é facilmente modificado para anexar o nome do arquivo como o último campo em vez do primeiro. por exemplo, altere as duas printdeclarações para:

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

Se você precisar inserir o campo nome do arquivo em algum outro lugar da linha, em vez de como o primeiro campo, você pode usar splicea função do Perl.

Por exemplo, o seguinte insere o nome do arquivo como o terceiro campo (observe que os índices da matriz perl começam em zero, não em 1, portanto, o terceiro campo é $F[2], não $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

Isso usa a opção do Perl -Fpara definir uma vírgula como delimitador de campo. Isso também permite que o recurso de divisão automática do Perl divida automaticamente a linha de entrada em um array chamado @F(isto é semelhante ao comportamento padrão do awk de dividir automaticamente a linha de entrada em $1, $2, $3, etc). A string literal "NAME" ou o nome do arquivo modificado é dividido em @F e, em seguida, os elementos da @Fmatriz são impressos, unidos por vírgulas.

Finalmente, se você quiser realmente alterar o conteúdo dos arquivos, use -ia opção do Perl. Opcionalmente, você pode manter um backup do arquivo original usando uma "extensão" com a -iopção, por exemplo, renomear filename.csvpara filename.csv.origwith -iorig. Por exemplo:

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

ou

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

Responder4

Em seguida, faça um loop nos nomes dos arquivos e imprima as colunas com o 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 
(...)

informação relacionada