首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >编写特定csv行的Bash脚本

编写特定csv行的Bash脚本
EN

Stack Overflow用户
提问于 2016-07-14 11:47:30
回答 3查看 117关注 0票数 0

我是另一个编写脚本的新手(刚刚发现了,这让我大吃一惊!)我想要做的是有一个脚本,将大量的.csv文件编译成一个bigfile.csv,移除头,并插入我自己的头。我发现了以下解决方案:

代码语言:javascript
运行
复制
awk 'FNR > 1' *.csv > bigfile.csv
sed -i 1i"Ident - MD,Node ID,Date,Time,Sub Seq#,NO2..." bigfile.csv

太棒了!但是,当我尝试使用这个文件进行分析时,我会因为行差而得到错误。我看了一下,确实,里面有一些疯狂的条目。

幸运的是,我希望从原始.csv文件中得到的每一行都有第一列"MD“条目。那么,有谁知道我如何告诉awk只从第一个单元格中包含"MD“的.csv文件中取线?

编辑:谢谢你的帮助,伙计们,这很有魅力!不幸的是,里面还有一些奇怪的数据

代码语言:javascript
运行
复制
CParserError: Error tokenizing data. C error: Expected 51 fields in line 6589, saw 54

有一个简单的调整,是否有办法再次只采取线与51个字段?

EN

回答 3

Stack Overflow用户

发布于 2016-07-14 12:05:55

我将在这里讨论一下,假设您在sed中添加的行实际上是您要去掉的标题。

如果是这样的话,我建议您跳过sed行,然后告诉awk删除不是第一行的文件的第一行。

接下来,如果只想在第一个字段中包含文本MD的行,可以使用简单的regex进行测试。

代码语言:javascript
运行
复制
awk -F, '
    FNR==1 && NR > 1 { next }  # skip the header on all but the first file
    NF != 51 { next }          # skip this line if field count is wrong
    $1 ~ /MD/                  # print the line if the first field matches
' *.csv > /path/to/outputfile.csv
  • -F,选项告诉awk使用逗号分隔字段。
  • NR是处理的记录总数,而FNR是当前文件中的当前记录号。
  • 没有命令的条件假设print为命令(打印当前行)。

当然,如果您愿意,可以将整个awk脚本放在一行上。为了便于阅读,我把它分开了。

如果您的outputfile.csv位于获取输入csv文件的"glob“目录中,那么请注意,新文件将由shell创建,而不是由awk创建,并且也可能作为输入文件处理。如果您计划使用>>将重定向附加到现有文件,这可能会引起关注。

更新

正如您已经提到的,您要添加的头与您去掉的标头不同,通过将awk脚本更改为如下所示,您仍然可以避免使用sed这样的单独命令:

代码语言:javascript
运行
复制
awk -F, '
    BEGIN {
      print "Ident - MD,Node ID,Date,Time,Sub Seq#,NO2..."
    }
    FNR==1 { next }            # skip the header on all files
    NF != 51 { next }          # skip this line if field count is wrong
    $1 ~ /MD/                  # print the line if the first field matches
' *.csv > /path/to/outputfile.csv

在处理任何输入行之前,将执行awk的BEGIN块中的命令,因此,如果在那里打印新的标题,它们将出现在(重定向)输出的开头。(请注意,如果要在所有输入处理后生成脚注/汇总/etc,则存在类似的END块。)

票数 4
EN

Stack Overflow用户

发布于 2016-07-14 11:54:57

代码语言:javascript
运行
复制
awk 'BEGIN{print "Ident - MD,Node ID,Date,Time,Sub Seq#,NO2..."}
     if(FNR > 1){print}' *.csv > bigfile.csv

FNR在每个文件之后重置那个awk进程,但是NR不和NR=FNR只用于第一个文件。

一个小插图(当然是用我的测试数据)

代码语言:javascript
运行
复制
$ cat f1
Name,Roll
A,10
B,5
5$ cat f2
Name,Roll
C,56
D,44
$ awk 'BEGIN{print "Naam,RollNo"}FNR > 1{print}' f*>final
$ cat final 
Naam,RollNo
A,10
B,5
C,56
D,44

Note

正如您所看到的,最后一个文件的新头被转到awk BEGIN部分,该部分在开始时才被执行。

来到您的目标

我想要的原始.csv文件中的每一行都有第一列"MD“项

代码语言:javascript
运行
复制
awk 'BEGIN{FS=",";print "Ident - MD,Node ID,Date,Time,Sub Seq#,NO2..."}
     if(FNR > 1 && $1 == "MD" && NF == 51){print}' *.csv > bigfile.csv

Notes

这一条与第一种一般情况没有什么区别。

  • 它引入了,作为现场分选器。
  • FNR > 1 && $1 == "MD"的意思是,只有当第一个字段是MD($1 == "MD")且字段数为51(NF == 51)时,我才不想要标题(FNR=1)和打印内容。

惯用方式

正如[ @ghoti ]在他的评论中提到的:

awk的“默认”命令已经是{print}

因此,上面的脚本可以重写为:

代码语言:javascript
运行
复制
awk 'BEGIN{FS=",";print "Ident - MD,Node ID,Date,Time,Sub Seq#,NO2..."}
         (FNR > 1 && NF == 51 && $1 == "MD")' *.csv > bigfile.csv
票数 3
EN

Stack Overflow用户

发布于 2016-07-14 11:57:41

一条花哨的单线邮轮会想:-

代码语言:javascript
运行
复制
awk -F',' 'NR > 1 && $1 ~ /^MD/ && NF == 51 { print }' *.csv > /someotherpath/bigfile.csv

使用完整的bash脚本的适当方法应该是类似于类似的东西,而不是花哨的一行:-

代码语言:javascript
运行
复制
#!/bin/bash

# Am assuming the the '.csv' files are a single ',' separated 

for i in *.csv; do
    [ -e "$i" ] || continue    # To handle when no input *.csv files present
    awk -F',' 'NR > 1 && $1 ~ /^MD/ && NF == 51  { print }' "$i" > /someotherpath/bigfile.csv
done

解决方案的关键是使用awkNR & NF变量,该变量跟踪行内的当前行和nth字段,因此理想情况下,NR > 1将跳过正在处理的标题部分,$1 ~ /^MD/只返回第一列以模式开头的行,NF ==51打印包含51个字段的行。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38373385

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档