sed可以替换换行符吗?


42

sed和换行符有问题吗?
我有一个包含以下内容的文件test.txt

aaaaa  
bbbbb  
ccccc  
ddddd  

以下内容不起作用:
sed -r -i 's/\n/,/g' test.txt

我知道我可以使用tr它,但是我的问题是为什么用sed似乎不可能。

如果这是逐行处理文件的副作用,那么我会对为什么会发生这种情况感兴趣。我认为grep删除新行。sed是否也一样?


1
在这种情况下,sed可能不是最好的工具(例如“ tr”)。有一些工具更直观,更易于阅读/维护,性能更好(尤其是在大数据上)等。...不要用锤子拧螺丝(即使它可以工作)。您可以在以下位置找到比较:http
//slash4.de/blog/python/sed-replace-newline-or-python-awk-tr-perl-xargs.html

2
tr将添加尾随,并输出未终止的行。最好是用paste代替:paste -sd , test.txt
斯特凡Chazelas

Answers:


48

使用GNU sed和提供POSIXLY_CORRECT的不在环境中(用于单行输入):

sed -i ':a;N;$!ba;s/\n/,/g' test.txt

https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n

  1. 通过创建标签 :a
  2. 通过将当前行和下一行添加到模式空间 N
  3. 如果我们在最后一行之前,则转到创建的标签$!ba$!表示不要在最后一行上这样做(因为应该有最后一条换行符))。
  4. 最后,替换用模式空间(即整个文件)上的逗号替换每个换行符。

这似乎表明问题是sed逐行读取。但是我不明白为什么会这样。它可以读取行并将新行字符(或最后一个字符)替换为
Jim

1
@jim看起来它不在匹配的缓冲区中,但是我不太会使用sed,也许其他人可以对此有所了解。我认为您应该使用该特定信息来扩展您的Q,这样人们就更有可能阅读它,并希望能回答。
Anthon

这导致ba: Event not found
krb686

@ krb686您指的是“这个”?您是否sed使用这些确切选项运行了上述命令?在什么test.txt 文件上?使用哪个版本的sed(try sed --version)?
Anthon 2015年

@Anthon对不起,我想我想说的是“ the”。我读了另一篇SO帖子,通知我csh要求我转义!。有趣的是,这仍然对我不起作用,我最终不得不!.csh脚本中加倍转义。因此,我现在确实没有问题,但是您知道为什么会这样吗?对我sed :a;N;$\\!ba;s/\n/ /g'
有用的

16

这适用于GNU sed

sed -z 's/\n/,/g' 

-z 自4.2.2起包含

注意 -z将定界符更改为空字符(\0)。如果您的输入不包含任何空字符,则整个输入将被视为一行。这可能有其局限性

为了避免替换最后一行的换行符,可以将其改回:

sed -z 's/\n/,/g;s/,$/\n/'

(这又是GNU sed语法,但没有关系,因为整个过程仅是GNU)


3
这也将替换结尾的换行符,这可能不是OP想要的...将结果与mikeserv的解决方案进行比较。
don_crissti

7

从Oracle网站:

sed实用程序通过依次将文件逐行读取到内存中来工作。然后,它将执行为该行指定的所有操作,并将该行放回内存中,以进行请求的更改后转储到终端。在对这一行进行所有操作之后,它将读取文件的下一行并重复该过程,直到完成文件为止。

基本上,这意味着因为sed正在逐行读取,所以换行符不匹配。

来自https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n的解决方案是:

sed ':a;N;$!ba;s/\n/,/g'

或者,在便携式版本中(;跳标记标签后无需连接)

sed -e ':a' -e 'N;$!ba' -e 's/\n/,/g'

该页面上提供了有关其工作原理的说明。


我使用了这种形式的修改形式来解析VPN日志,并将用户“已认证”和时间戳记信息放在同一行上。干杯!
user208145

请注意,语法是GNU特定的,即使使用GNU sed,如果POSIXLY_CORRECT在环境中并且输入只有一行,那么将没有输出。
斯特凡Chazelas

5

sed总是\n在填充模式空间之前删除尾随ewline,然后在写出其脚本结果之前追加尾随ewline。\n可以通过多种方法在模式空间中使用ewline-但是如果不是编辑结果,则永远不会。这一点很重要- \n在ewlines sed的模式空间总是反映的变化,在输入流中永远不会发生。\newlines是sedder可以依靠未知输入输入的唯一定界符。

如果\n要用逗号替换所有行,而文件不是很大,则可以执行以下操作:

sed 'H;1h;$!d;x;y/\n/,/'

h它将每条输入行附加到旧空间(第一行除外),第一行后跟一行字符,然后覆盖h旧空间\n。然后,它从输出中d删除每一行而不是$!最后一行。在最后一行上Hx更改了旧的空格和模式空格,并将所有\newline字符y///转换为逗号。

对于大文件,这种事情势必会引起问题- sed行边界上的缓冲区,这种行为很容易溢出。


2

另外,您可以使用稍微简单一些的语法:

sed ':a;N;s/\n/,/g;ba'

...只是改变顺序。


3
但是s,在越来越大的模式空间上为每个输入行运行命令。
斯特凡Chazelas

1

这里有一些非常好的sed魔术。还有一些关于模式空间溢出的优点。我喜欢即使不是最简单的方法也可以使用sed,因为它是如此的紧凑和强大。但是,它有其局限性,对于大量数据,模式空间必须是笨拙的。

GNU这样说:

对于那些想要编写可移植sed脚本的人,请注意,已知某些实现将行长(用于模式和保留空间)限制为不超过4000字节。posix标准指定符合sed的实现应至少支持8192字节的行长。GNU sed对行长没有内置限制。只要它可以malloc()更多(虚拟)内存,就可以根据需要提供或构造行。
但是,递归用于处理子模式和不确定的重复。这意味着可用的堆栈空间可能会限制某些模式可以处理的缓冲区的大小。

我没有什么要补充的,但是我想向您指出我的sed入门指南。太好了 http://www.grymoire.com/Unix/Sed.html

这是我的解决方案:

for i in $(cat test.txt); do echo -n $i','; done; echo '' >> somewhere

很好



-1

假设您要用替换换行符\n。我想这样做,所以这是我做的:

(echo foo; echo bar; echo baz) | sed -r '$!s/$/\\n/' | tr -d '\n' 
# Output: foo\nbar\nbaz

它的作用是:对于除last之外的所有行,append \n。然后,使用删除换行符tr


-r仅在GNU中可用sed,而在BSD中不可用。
kenorb
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.