文本处理-每两行用逗号连接


35

我的文件中有1000多行。该文件开始如下(添加行号):

Station Name
Station Code
A N DEV NAGAR
ACND
ABHAIPUR
AHA
ABOHAR
ABS
ABU ROAD
ABR

我需要将其转换为文件,并通过每两行连接以逗号分隔的条目。最终数据应如下所示

Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR
...

我正在尝试的是-尝试编写Shell脚本,然后echo在两者之间加逗号。但我想更简单有效的一行将在这里做的工作可能在sed/ awk

有任何想法吗?


@ l0b0您编辑了OP的注释,即行号“仅用于解释” ...
jasonwryan 2012年

@jasonwryan抱歉,我认为这些可以解释。在0行解析错误
l0b0

Answers:


39

只需使用cat(如果您喜欢猫;-))和paste

cat file.in | paste -d, - - > file.out

说明:paste从多个文件中读取并粘贴相应的行(第一个文件的第1行与第二个文件的第1行,等等):

paste file1 file2 ...

除了文件名,我们可以使用-(破折号)。paste从file1(它是stdin)获取第一行。然后,它想从file2(也是stdin)读取第一行。但是,由于已经读入并处理了stdin的第一行,所以现在等待输入流的是stdin 的第二行,它很paste高兴地粘在第一行上。该-d选项将分隔符设置为逗号而不是制表符。

或者,做

cat file.in | sed "N;s/\n/,/" > file.out

PS是的,可以简化以上内容

< file.in sed "N;s/\n/,/" > file.out

要么

< file.in paste -d, - - > file.out

优点是不使用cat

但是,出于清晰的原因,我没有故意使用此惯用语-它不太冗长,我喜欢cat(CATS ARE NICE)。所以请不要编辑。

另外,如果您喜欢粘贴而不是cats(paste是将文件水平连接的命令,而cat是垂直连接文件的命令),则可以使用:

paste file.in | paste -d, - -

再说一遍。行号不是文件的一部分:)
mtk 2012年

paste 命令非常有效,请您提供一些解释。连字符???
mtk 2012年

2
连字符表示“从stdin读取”。如果重复相同的输入源,那么粘贴知道每行输出要多次读取它。
dubiousjim 2012年

@sch:很酷的编辑,我不会碰它的:-)
1

1
关于你的cat论点。难道sed "N;s/\n/,/" file.in > file.out不行?
伯恩哈德(Bernhard)2012年

8

如果有人登陆这里希望将所有行合并为一个CSV班轮,请尝试

cat file | tr '\n' ','

3
sed 'N;s/\n/,/' file

使用sed,每两行加入一次(N),并将换行符(\ n)替换为“,”。


3
paste -sd ',\n' file.in > file.out

还要注意,因为我们只是将一个字符替换为另一个字符(其他每个换行符都用逗号替换),所以我们可以就地处理输入文件:

paste -sd ',\n' file.in 1<> file.in

(但是请注意,它可能无法在某些具有CRLF终结符(例如Microsoft终结符)的非Unix系统上运行,某些仿真的POSIX paste可能会以非Unix方式对待终结符)


1是在做什么1<>?那是错字吗?
αғsнιη

@αғsнιη,看到这个
iruvar,

@iruvar谢谢
αғsнιη

2

这是使用纯Bash的单行代码(尽管可能有数百万条命令运行):

(IFS=; while read -r name; do read -r code; printf '%s\n" "$name,$code"; done < file.in) > file.out

我使用了一个subshel​​l(寄生),因此不必存储和恢复IFS。如果源是源,那么应该采取另一种措施以免弄乱用户环境。另一种方法是通过新的IFS只能readIFS= read -r nameIFS= read -r code

循环中的所有命令都内置在Shell中的事实使它的性能可以接受,甚至比其他针对小文件的解决方案还快。但是许多人会认为这是一种不好的做法,将其推广到其他任何事物时都应该小心。


通常,是使用子外壳来本地化环境更改。但是在这种情况下,它是不需要的:您可以改为做while IFS='\n' read -r name; do IFS='\n' read -r code ... done < file.in,这是我在Shell脚本中经常看到的一个习惯用法。该-r标志read的装置“解释随后STDIN流中的字符‘n’个为两个字符,而不是作为一个新行的字符‘\’。” 可以说,与重复创建相比,在创建子外壳时可能更美观IFS='\n'
dubiousjim 2012年

@dubiousjim:从-r技术上改进了解决方案。大!我不喜欢通过IFS两次更改的想法。如果我读过一次,超级好,但没有两次。当然,这是见仁见智。我会说,使用subshel​​l有点不了解Bash的一般知识,因此很多人都难以理解其目的。那是一件坏事。
2012年

2

对于完整的答案,可能的awk解决方案可能是:

awk 'NR%2==1 {printf $0","} NR%2==0 { print $0}' *file*

@downvoter:我的答案到底该怎么办?如何改善?
伯恩哈德(Bernhard)2012年

也许是因为偷懒printf?当工作站名称包含格式说明符时,在极少数情况下将失败。(有关示例,请参见pastebin.com/wgxFttrJ。)但这只是一个猜测,反对派不是我的。
manatwork,2012年

1

awk惯用的成语老栗子

awk '{ORS=NR%2?",":"\n";print}' file
Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR

awk '{ORS=NR%2?",":"\n"};1'更短更成语
cuonglm

@cuonglm,我对此表示怀疑。在这种情况下,尽管存在print且意图很明确,但它仍然是单线的。1awk像我这样的老手也很清楚,但我更喜欢print
iruvar

这是我发现的第一个简单的解决方案,可以轻松地将其配置为2条以上的线路。sed在搜索之前,我与之作战了一段时间,但awk使每4行的合并变得更加容易。救了我一趟$EDITOR
opello

0

也可以用perl,

perl -pe 's/^\d+\.\s+//;$.&1?chomp:print","' file


0

例如:

seq 0 70 | xargs -L 2 | sed 's/ /,/g'

输出:(注意: xargs -L number_of_columns大多数列数都能很好地工作,而不仅仅是每两行)

0,1
2,3
4,5
6,7
8,9
10,11
12,13
14,15
16,17
18,19
20,21
22,23
24,25
26,27
28,29
30,31
32,33
34,35
36,37
38,39
40,41
42,43
44,45
46,47
48,49
50,51
52,53
54,55
56,57
58,59
60,61
62,63
64,65
66,67
68,69
70

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.