
警告:絕對的初學者。我需要在 .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
使用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
您可以在 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
然後
替換目前
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
新增單獨的
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
新增一
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 檔案的內容列印到標準輸出。
與 不同awk
,perl
不會追蹤每個單獨檔案的行數(它僅追蹤帶有變數的總行數$.
)。該腳本手動維護該計數,首先在 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
(...)