1

我陷入了看起来对 SED 来说应该很简单的事情。

我有一些(某种)CSV 文件是从另一个应用程序中获取的,因此我无法控制它的输出。SED 已经完成了一些预处理,但我坚持最后一个。因此,如果可能的话,我希望使用 SED 来避免使用第三个应用程序。

问题是文件的标题行(第一行)沿文件重复,但不幸的是具有以下特征:

  1. 每个 CSV 文件的标题以前是未知的。每个文件都有自己的标题,可能彼此不同;
  2. 并非总是在每 N 行上发生重复(N 是一个固定的已知数)
  3. 其他数据(非标题)行可能会重复,应保留

所以,假设我有以下 2 个文件:

现金.csv

Name; Amount
John; 3.55
Erick; 4.76
John; 8.99
Name; Amount
Erick; 4.76
Mark; 1.00
Name; Amount
John; 3.55

检查.csv

Name; Account; Amount
Erick; 345344; 123.00
Mark; 88849; 323.50
Name; Account; Amount
John; 474473; 99.00
Mark; 88849; 323.50
Mark; 88849; 323.50
John; 474473; 99.00

我希望将单个 SED 脚本应用于每个文件,将它们变成:

Cash.processed.csv

Name; Amount
John; 3.55
Erick; 4.76
John; 8.99
Erick; 4.76
Mark; 1.00
John; 3.55

检查.processed.csv

Name; Account; Amount
Erick; 345344; 123.00
Mark; 88849; 323.50
John; 474473; 99.00
Mark; 88849; 323.50
Mark; 88849; 323.50
John; 474473; 99.00

我想知道是否可以在删除命令中使用 SED“保持缓冲区”作为模式:

1h     #Hold the first line (headings)
/\h/d  #Use hold buffer as a pattern to delete

假设“\h”会将保持缓冲区返回给删除命令。

感谢您的任何回复;

PS:请不要用以下过于具体的命令回答:

1p;/Name; Amount\|Name; Account; Amout/d
4

3 回答 3

4

我认为您需要从一个sed命令中捕获第一行,然后在主要操作命令中使用它:

line1=$(sed 1q $datafile)

sed -e "2,$ {/$line1/d;}" \
    -e '...rest of sed script...' $datafile

因为sed 1q读完第一行就退出了,不管数据文件有多大,都很快。如果第一行有可能包含斜杠(可能是标题"Name/Number")或其他正则表达式元字符,那么请考虑使用类似这样的东西,它将所有斜杠替换为.

line1=$(sed '1{s%/%.%g;q;}' $datafile)

我对 Mac OS X (10.8.1) 版本进行了一些sed操作,它比 GNU 更麻烦sed。在第二个(主)sed命令中,匹配必须是 in {...},美元必须是分开的(或者 shell 对无效的参数替换感到不安),并且需要分号。GNU 可能不需要其中一些限制sed,但显示的代码可能在任何地方都可以使用。

于 2012-08-25T14:46:14.403 回答
2

这可能对您有用(GNU sed):

sed '1h;1!{G;/^\(.*\)\n\1/d;s/\n.*//}' file

解释:

  • 1h将标题行存储在保持空间 (HS) 中并打印。
  • 1!{G;/^\(.*\)\n\1/d;s/\n.*//}对于除第一行之外的每一行,附加一个换行符,后跟 HS 的内容(即标题行)。将该行的第一部分与标题行进行比较,如果相同,则删除该行。如果不是删除附加的换行符和标题行并正常打印。

编辑:

这在大文件上确实非常慢,一个更快且可能更容易理解的解决方案是:

sed 's|.*|1!{/^&$/d}|;q' file | sed -f - file

这会从输入文件的第一行生成一个 sed 脚本。

于 2012-08-25T16:08:28.667 回答
1

如果您对 awk 感兴趣:

awk '{if(NR==1){p=$0;print}if(NR>1 && p!=$0)print}' your_file
于 2012-08-25T16:54:59.713 回答