如何使用sed或awk操作CSV文件?


23

如何使用sed或对CSV文件执行以下操作awk

  • 删除栏
  • 复制列
  • 移动列

我有一张大桌子,上面有200多行,但我并不熟悉sed


1
十字架在AskUbuntu上发布
enzotib 2011年

@enzotib您可以发布链接吗?
n0pe 2011年

@ MaxMackieaskubuntu.com/ questions/ 88142/…。我现在无法在那儿找到一个mod,所以我标记为要求他们是否愿意迁移。它已经有一个可接受的答案,所以我不确定他们是否会
Michael Mrozek

@MichaelMrozek,嗯,在这些情况下通常会发生什么?我们只是保留重复项吗?
n0pe 2011年

1
除非您需要在仅具有基本工具的系统上运行,否则请参阅是否存在用于处理csv文件的强大命令行工具?
吉尔(Gilles)“所以,别再邪恶了”

Answers:


7

除了如何剪切和重新排列字段(在其他答案中都有介绍)之外,还存在古怪的CSV字段的问题。

如果您的数据属于“怪异”类别,则可以进行一些前置后置过滤。如下图所示过滤器所需要的字符\x01\x02\x03\x04不要在任何地方你的数据出现。

这是围绕简单awk字段转储的过滤器。

注意: 字段五具有无效/不完整的“引用字段”布局,但在行末尾是良性的(取决于CSV解析器)。但是,当然,如果将其从当前行尾位置替换掉,则会导致无法预料的结果

更新;user121196指出了逗号在尾随引号之前的错误。解决方法是这里。

数据

cat <<'EOF' >file
field one,"fie,ld,two",field"three","field,\",four","field,five
"15111 N. Hayden Rd., Ste 160,",""
EOF

代码

sed -r 's/^/,/; s/\\"/\x01/g; s/,"([^"]*)"/,\x02\1\x03/g; s/,"/,\x02/; :MC; s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g; tMC; s/^,// ' file |
  awk -F, '{ for(i=1; i<=NF; i++) printf "%s\n", $i; print NL}' |
    sed -r 's/\x01/\\"/g; s/(\x02|\x03)/"/g; s/\x04/,/g' 

输出:

field one
"fie,ld,two"
field"three"
"field,\",four"
"field,five

"15111 N. Hayden Rd., Ste 160,"
""

这是前置过滤器,带有注释。
后置滤波器只是一个逆转\x01\x02\x03\x04

sed -r '
    s/^/,/                # add a leading comma delimiter
    s/\\"/\x01/g          # obfuscate escaped quotation-mark (\")
    s/,"([^"]*)"/,\x02\1\x03/g    # obfuscate quotation-marks
    s/,"/,\x02/           # when no trailing quote on last field  
    :MC                   # obfuscate commas embedded in quotes
    s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g
    tMC
    s/^,//                # remove spurious leading delimiter
'

您将如何基于此过滤器删除第n列?
user121196

@ user121196-如开头句中所述,此答案显示了一种使CSV数据更一致的方法。通过用中性标记字符临时替换用引号引起来的逗号...,然后在移动/剪切/删除后将其还原为逗号。再次提到,移动/剪切/删除步骤被简单的awk field-dump替换。
Peter.O 2012年

1
它在这种情况下失败:“ 15111 N. Hayden Rd。,Ste 160,“,””
user121196 2012年

@ user121196:感谢您指出这一点。我已经修复了答案。
Peter.O 2012年

15

这取决于您的CSV文件是仅将逗号用作分隔符,还是您是否像这样疯狂:

第一场,“第二场”,第三场

假设您使用的是简单的CSV文件:

删除列

您可以通过多种方法摆脱单个列;我以第2列为例。最简单的方法可能是使用cut,它使您可以指定分隔符-d以及要打印的字段-f;这告诉它在逗号和输出字段1以及最后的字段3上进行拆分:

$ cut -d, -f1,3- /path/to/your/file

如果您确实需要使用sed,则可以编写一个匹配第一个n-1字段,第nth个字段和其余字段的正则表达式,并跳过输出nth(这里n为2,因此第一个组与1time:匹配\{1\}):

$ sed 's/\(\([^,]\+,\)\{1\}\)[^,]\+,\(.*\)/\1\3/' /path/to/your/file

有很多方法可以做到这一点awk,但是没有一种方法特别优雅。您可以使用for循环,但是处理尾随的逗号会很痛苦。忽略它是这样的:

$ awk -F, '{for(i=1; i<=NF; i++) if(i != 2) printf "%s,", $i; print NL}' /path/to/your/file

我发现输出字段1然后substr在字段2之后提取所有内容更容易:

$ awk -F, '{print $1 "," substr($0, length($1)+length($2)+3)}' /path/to/your/file

尽管这对于后续的列还是很烦人的

复制列

sed此表达式中,该表达式基本上与以前相同,但是您还捕获了目标列,并多次在替换中包含该组:

$ sed 's/\(\([^,]\+,\)\{1\}\)\([^,]\+,\)\(.*\)/\1\3\3\4/' /path/to/your/file

awkfor循环方式中,它类似于(再次忽略结尾的逗号):

$ awk -F, '{
for(i=1; i<=NF; i++) {
    if(i == 2) printf "%s,", $i;
    printf "%s,", $i
}
print NL
}' /path/to/your/file

substr方式:

$ awk -F, '{print $1 "," $2 "," substr($0, length($1)+2)}' /path/to/your/file

(tcdyl在他的回答中想出了一个更好的方法)

移动列

我认为sed解决方案很自然地遵循了其他解决方案,但是它开始变得可笑的长


这是一个加载的答案!+1 :)
jaypal singh 2011年


12

awk是你最好的选择。awk按数字打印字段,所以...

awk 'BEGIN { FS=","; OFS=","; } {print $1,$2,$3}' file

要删除列,请不要打印它:

 awk 'BEGIN { FS=","; OFS=","; } {print $1,$3}' file

更改顺序:

awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file

重定向到输出文件。

awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file > output.file

awk 也可以格式化输出。

AWK格式输出


由于是CSV,因此您还需要BEGIN { FS=","; OFS=","; }

1
我认为即使FS = OFS =“,”也可以。

5

给定以空格分隔的文件,格式如下:

1 2 3 4 5

您可以使用awk删除字段2,如下所示:

awk '{ sub($2,""); print}' file

哪个返回

1  3 4 5

将第2列替换为第n列。

要复制第2列,

awk '{ col = $2 " " $2; $2 = col; print }' file

哪个返回

1 2 2 3 4 5

要切换第2列和第3列,

awk '{temp = $2; $2 = $3; $3 = temp; print}'

哪个返回

1 3 2 4 5

awk通常非常擅长处理字段的概念。如果您要处理的是CSV文件而不是空格文件,则只需使用

awk -F,

将您的字段定义为逗号,而不是空格(这是默认值)。在线上有许多很好的awk资源,我在下面列出其中之一。

#3的来源


我不太了解awk,但是即使使用字段分隔符,它似乎也会以空格分隔,(字段分隔符仅控制其如何处理输入)
Michael Mrozek

@MichaelMrozek:是的,它是控制输出字段分隔符的OFS awk变量。
enzotib

是的,正如我在回答中提到的那样,您可以将-F选项传递给awk来更改分隔符(例如-F,)
tcdyl 2011年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.