警告:绝对的初学者。我需要向 .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
(...)