如何使用sed
或对CSV文件执行以下操作awk
?
- 删除栏
- 复制列
- 移动列
我有一张大桌子,上面有200多行,但我并不熟悉sed
。
如何使用sed
或对CSV文件执行以下操作awk
?
我有一张大桌子,上面有200多行,但我并不熟悉sed
。
Answers:
除了如何剪切和重新排列字段(在其他答案中都有介绍)之外,还存在古怪的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
'
这取决于您的CSV文件是仅将逗号用作分隔符,还是您是否像这样疯狂:
第一场,“第二场”,第三场
假设您使用的是简单的CSV文件:
您可以通过多种方法摆脱单个列;我以第2列为例。最简单的方法可能是使用cut
,它使您可以指定分隔符-d
以及要打印的字段-f
;这告诉它在逗号和输出字段1以及最后的字段3上进行拆分:
$ cut -d, -f1,3- /path/to/your/file
如果您确实需要使用sed
,则可以编写一个匹配第一个n-1
字段,第n
th个字段和其余字段的正则表达式,并跳过输出n
th(这里n
为2,因此第一个组与1
time:匹配\{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
在awk
for循环方式中,它类似于(再次忽略结尾的逗号):
$ 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
解决方案很自然地遵循了其他解决方案,但是它开始变得可笑的长
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
也可以格式化输出。
给定以空格分隔的文件,格式如下:
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
,但是即使使用字段分隔符,它似乎也会以空格分隔,
(字段分隔符仅控制其如何处理输入)