新增列,用檔案名稱填入整個列(不含“.csv”) - 對於 BaSH 中的多個文件

新增列,用檔案名稱填入整個列(不含“.csv”) - 對於 BaSH 中的多個文件

警告:絕對的初學者。我需要在 .csv 檔案中新增一列,其中列標題可以是“名稱”,但整個列應該完全相同 - 檔案本身的名稱,.csv 檔案的名稱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

使用awkcolumn

$ 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

您可以在 shell 循環中執行此命令,將修改後的檔案儲存到目錄中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(沒有逗號之內字段等 -如果需要完整的 RFC-4180 支持,您可以更改--csvlite為)--csv

$ 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 glob ex 一次傳遞多個檔案。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 檔案的內容列印到標準輸出。

與 不同awkperl不會追蹤每個單獨檔案的行數(它僅追蹤帶有變數的總行數$.)。該腳本手動維護該計數,首先在 BEGIN 區塊中設定變量$fnr,然後為讀取的每一行遞增該變量,最後每次到達檔案末尾時將其重設為 1。

這很容易修改為將檔案名稱附加為最後一個欄位而不是第一個欄位。例如將兩個print語句更改為:

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

如果您需要將文件名字段插入行中的其他位置,而不是作為第一個字段,您可以使用 perl 的splice函數。

例如,以下將檔案名稱插入為第三個欄位(請注意,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列印陣列的元素,並用逗號字元連接。

最後,如果您想實際更改文件的內容,請使用 perl 的-i選項。您可以選擇使用帶有選項的「副檔名」來保留原始檔案的備份-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 
(...)

相關內容