在每行文本中,去除两个外部分隔符之间的多个分隔符。

12
使用awk或sed在bash脚本中,我需要删除位于内部和外部分隔符之间的逗号分隔符。问题是错误的值最终出现在错误的列中,只有3列是期望的。
例如,我想把这个变成:
2020/11/04,Test Account,569.00
2020/11/05,Test,Account,250.00
2020/11/05,More,Test,Accounts,225.00

转化为:

2020/11/04,Test Account,569.00
2020/11/05,Test Account,250.00
2020/11/05,More Test Accounts,225.00

我尝试使用一些东西来测试正则表达式: 但是我找不到仅选择逗号以删除的解决方案。

regexr示例


没有人建议你修复生成不正确CSV数据的上游流程。 - glenn jackman
1
这里有许多使用正则表达式的答案,因此保留此标签以增强对该问题的搜索能力是个好主意。 - anubhava
感谢Anubhav先生添加标签,OP已经说明这是一个正则表达式问题,不确定为什么标签被删除了。 - RavinderSingh13
7个回答

9
awk -F, '{ printf "%s,",$1;for (i=2;i<=NF-2;i++) { printf "%s ",$i };printf "%s,%s\n",$(NF-1),$NF }' file

使用awk,打印第一个逗号分隔的字段,然后循环遍历其余字段直到倒数第二个字段,打印该字段后跟一个空格。然后对于最后2个字段,打印倒数第二个字段、逗号和最后一个字段。

8

使用GNU awk的match()函数的第三个参数:

$ awk -v OFS=, '{
     match($0,/([^,]*),(.*),([^,]*)/,a)
     gsub(/,/," ",a[2])
     print a[1], a[2], a[3]
}' file
2020/11/04,Test Account,569.00
2020/11/05,Test Account,250.00
2020/11/05,More Test Accounts,225.00

或者使用任何awk:
$ awk '
    BEGIN { FS=OFS="," }
    {
        n = split($0,a)
        gsub(/^[^,]*,|,[^,]*$/,"")
        gsub(/,/," ")
        print a[1], $0, a[n]
    }
' file
2020/11/04,Test Account,569.00
2020/11/05,Test Account,250.00
2020/11/05,More Test Accounts,225.00

7
使用这个 Perl 一行命令:
perl -F',' -lane 'print join ",", $F[0], "@F[1 .. ($#F-1)]", $F[-1];' in.csv

这个 Perl 一行命令使用了这些命令行标志:
-e :告诉 Perl 在行内查找代码,而不是在文件中查找。
-n :逐行遍历输入,默认将其赋值给$_
-l :执行行内代码前剥离输入行分隔符(默认为*nix上的"\n"),并在打印时追加它。
-a :在空格或规定在-F选项里的正则上,将$_分割为@F数组。
-F',' :根据逗号而非空格,将$_拆分成@F数组。 $F[0]:数组@F的第一个字段(第一个逗号分隔的值)。
$F[-1]:数组@F的最后一个元素。
@F[1 .. ($#F-1)]:从前数第二个元素到末尾倒数第二个元素之间的@F元素,包括这两个元素本身。
"@F[1 .. ($#F-1)]":上述元素按空格连接在一起的字符串。
join ",", ...:用逗号将列表 "..." 中的元素拼接成一个字符串,并返回结果。 另见:
perldoc perlrun:如何执行 Perl 解释器的命令行开关

7
perl -pe 's{,\K.*(?=,)}{$& =~ y/,/ /r}e' file

sed -e ':a' -e 's/\(,[^,]*\),\([^,]*,\)/\1 \2/; t a' file

awk '{$1=$1","; $NF=","$NF; gsub(/ *, */,","); print}' FS=, file

awk '{for (i=2; i<=NF; ++i) $i=(i>2 && i<NF ? " " : ",") $i} 1' FS=, OFS= file

6

awk不支持正则表达式中的look around,但我们可以通过使用 awkmatch函数来实现。请尝试以下代码,它已在GNU awk中使用示例进行了编写和测试。

awk '
match($0,/,.*,/){
  val=substr($0,RSTART+1,RLENGTH-2)
  gsub(/,/," ",val)
  print substr($0,1,RSTART) val substr($0,RSTART+RLENGTH-1)
}
' Input_file

6

又一个perl

$ perl -pe 's/(?:^[^,]*,|,[^,]*$)(*SKIP)(*F)|,/ /g' ip.txt
2020/11/04,Test Account,569.00
2020/11/05,Test Account,250.00
2020/11/05,More Test Accounts,225.00
  • (?:^[^,]*,|,[^,]*$) 匹配第一个/最后一个字段以及逗号字符
    • (*SKIP)(*F) 这将防止修改前面的正则表达式
  • |, 提供,作为备用正则表达式,以便进行修改匹配

使用sed(假设实现支持\n,否则,您将不得不找到输入中不存在的字符)

sed -E 's/,/\n/; s/,([^,]*)$/\n\1/; y/,/ /; y/\n/,/'
  • s/,/\n/; s/,([^,]*)$/\n\1/ 将第一个和最后一个逗号替换为换行符
  • y/,/ / 将所有逗号替换为空格
  • y/\n/,/ 将换行符改回逗号

1

在awk中,类似于Timur's的答案

awk '
    BEGIN { FS = OFS = "," }
    function join(start, stop, sep,     str, i) {
        str = $start
        for (i = start + 1; i <= stop; i++) {
            str = str sep $i
        }
        return str
    }
    { print $1, join(2, NF-1, " "), $NF }
' file.csv

很遗憾,awk没有内置的join函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接