добавить столбец, заполнить весь столбец именем файла (без «.csv») — для нескольких файлов в BaSH

добавить столбец, заполнить весь столбец именем файла (без «.csv») — для нескольких файлов в BaSH

Предостережение: Абсолютный новичок. Мне нужно добавить столбец в файл .csv, где заголовок столбца может быть "Name", но весь столбец должен быть точно таким же — имя самого файла, filename. Теперь в каждом файле всего 3 переменные, но 2100 строк.

Пример: Для файла"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

Это то, чего я хочу -

  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

Или это -

  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

Если бы я мог просто заменить все данные в столбце "HUC8" на это filenameбыло бы идеально. Это не обязательно должен быть дополнительный столбец.

Мне нужно сделать это для многих тысяч файлов.

Если бы я знал, как сделать первую часть, я бы мог создать цикл. Но, может быть, есть еще лучший способ?

Я не знаю, с чего начать.

решение1

Используя awkи 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

Вы можете запустить это в цикле оболочки, чтобы сохранить измененные файлы в каталоге 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

Если вам нужно заменить столбец HUC8и это не первый столбец, измените код на следующий:

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

решение2

С использованиемМиллер, и предположим, что ваши файлы представляют собой «простые» CSV (без запятыхв пределахполя и т. д. - можно изменить --csvliteна , --csvесли требуется полная поддержка 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

затем

  1. для замены текущего HUC8столбца:

     $ 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. чтобы добавить отдельный Nameстолбец:

     $ 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. чтобы добавить Nameстолбец в качестве первого столбца:

     $ 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
    

Все вышеперечисленное записывает результат в стандартный вывод - чтобы изменить файл на месте, добавьте опцию -I. Вы можете передать несколько файлов одновременно, используя shell globs, например, bcc*.csvили *.csv.

[При тестированиибез -Iстрока заголовка не будет повторяться, если только не потребуется новый заголовок из-за неоднородности записи; однако -Iсоответствующий заголовок будет добавлен к каждому файлу.]

решение3

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

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

             $fnr++;

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

Это добавит имя файла (без «расширения» .csv) в качестве первого поля и выведет содержимое файлов .csv в стандартный вывод.

В отличие от awk, perlне отслеживает количество строк для каждого отдельного файла (отслеживает только общее количество строк с $.переменной). Этот скрипт поддерживает это количество вручную, сначала устанавливая переменную $fnrв блоке BEGIN, затем увеличивая ее для каждой прочитанной строки и, наконец, сбрасывая ее обратно на 1 каждый раз, когда достигается конец файла.

Это легко изменить, добавив имя файла в качестве последнего поля вместо первого. Например, измените два printоператора на:

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

Если вам нужно вставить поле имени файла в другое место строки, а не в первое поле, вы можете использовать spliceфункцию Perl.

Например, следующий код вставляет имя файла в качестве третьего поля (обратите внимание, что индексы массива Perl начинаются с нуля, а не с 1, поэтому третье поле — $F[2], а не $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

Здесь используется опция perl -Fдля установки запятой в качестве разделителя полей. Это также включает функцию авторазбиения perl для автоматического разбиения входной строки на массив с именем @F(это похоже на поведение awk по умолчанию, когда входная строка автоматически разбивается на $1, $2, $3 и т. д.). Либо литеральная строка "NAME", либо измененное имя файла объединяются в @F, затем элементы массива @Fвыводятся, соединяясь запятыми.

Наконец, если вы хотите изменить содержимое файлов, используйте -iпараметр perl. Вы можете по желанию сохранить резервную копию исходного файла, используя "расширение" с параметром -i, например, переименовать filename.csvв filename.csv.origс помощью -iorig. Например:

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

или

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

решение4

Затем выполните цикл по именам файлов и выведите столбцы с помощью 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 
(...)

Связанный контент