2016-01-06 167 views
0

OSX上的sed有一些怪癖。该资源(http://nlfiedler.github.io/2010/12/05/newlines-in-sed-on-mac.html)包含有关如何空格转换成一个换行符信息:OSX sed换行符 - 为什么将空格转换为换行符,但换行符不会转换为空格

echo 'foo bar baz quux' | sed -e 's/ /\'$'\n/g' 

OR(@ ghoti的建议,这也使其更易于阅读):

echo 'foo bar baz quux' | sed -e $'s/ /\\\n/g' 

然而,当我尝试反向 - 换行转换到空白,这是行不通的:

echo -e "foo\nbar" | sed -e 's/\'$'\n/ /g' 

的只是改变\n更简单的方法行不通eithe R:

echo -e "foo\nbar" | sed -e 's/\n/ /g' 

有一个相关的答案在这里:https://superuser.com/questions/307165/newlines-in-sed-on-mac-os-x,由斯皮夫了详细的解答(右页面结束),但是应用相同的逻辑并没有解决问题。

这里是一个没有在OSX工作(通过http://www.benjiegillam.com/2011/09/using-sed-to-replace-newlines/)的方式:

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

不过,我仍然好奇,为什么扭转了原来的做法是行不通的。

更新:这里是如何使它与两行的工作(该解决方案是使用N嵌入换行符):

echo -e "foo\nbar\n" | sed -e 'N;s/\n/ /g' 

的替代解决方案(见@ghoti详细解释完整的答案) :

echo -e "foo\nbar\n" | sed -n '1h;2,$H;${;x;s/\n/ /gp;}' 

然而,这种解决方案似乎是一点点慢于一个问题中陈述建议(这些命令的事项记顺序,因此它可能是有意义的尝试以不同的顺序测试它们):

time seq 10000 | sed -n '1h;2,$H;${;x;s/\n/ /gp;}' > /dev/null 

time seq 10000 | sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g' > /dev/null 
+0

用三行输入尝试更新的解决方案。并查看下面的答案。 :) – ghoti

+0

另外,如果你没有看到这个,你的第一个例子''/'\'''\ n/g''实际上依赖于shell扩展。 (请查看友好的邻居bash手册页的QUOTING部分。)它将在bash和其他一些shell中工作,但不是通用的(或POSIX)。您可以通过对整个表达式使用相同的引用样式来使其更清晰或更易于阅读:'sed -e $'s// \\\ n/g''。 – ghoti

回答

1

您的问题似乎是“为什么不将原始方法(将空格转换为换行符)的工作原理相反?”。

在sed中,换行符比行的一部分更像是一个记录分隔符。考虑$,即模式空间末尾的null,位于该行的最后一个字符之后,并且不是每行的换行符。

使用换行符的Sed命令(如HN甚至s)在换行符作为记录分隔符的范围之外。您要替换的记录位于换行符之间。

为了替代换行符,那么,你需要把它里面的模式空间,使用NH

因此,这里是一个选项。

printf 'foo\nbar\nbaz\n' | sed -n '1h;2,$H;${;x;s/\n/ /gp;}' 

的想法是,我们将我们所有的行追加到保持缓冲器,然后在文件的结尾,将保持缓冲器回用于替代模式空间和空格替换所有的换行立刻。

1h;2,$H构造避免了在输出开始时出现空白,这是由于在每行数据前附加了换行符H

+0

谢谢!它可以工作,但是我想知道'$ {}'是否会给性能带来额外的压力? – econ

+0

我怀疑如果有性能影响,它将会在非常大的文件上,因为使用此解决方案,sed需要将所有输入数据存储在内存中(保持缓冲区),直到它到达文件末尾。如果你想要更精简的东西,也许'awk'{printf(“%s”,$ 0)}''将会是更好的选择,或者'tr'\ n''''如果这足够满足你的需求。但是这又解决了。你的问题的答案是上面的部分,关于换行符作为分隔符而不是字符串的一部分。 – ghoti

1

的GNU手册页sed包括:

正则表达式

POSIX.2 BREs里面应该支持,但他们也不是完全因为性能问题。正则表达式中的\n序列与换行符匹配,并且类似地为\a\t和其他序列。

的Mac OS X中的sed手册页包括:

桑达正则表达式

sed使用,默认情况下,正则表达式,是基本的正则表达式(BREs里面,见请参阅re_format (7)以获取更多信息),但如果给出-E标志,则可以使用扩展(现代)正则表达式。此外,sed有以下两个补充的正则表达式:

  1. 在上下文地址,比反斜杠(\)或换行字符以外的任何字符可以被用于分隔正则表达式。此外,在分隔字符之前放置反斜杠字符会导致字符被逐字处理。例如,在上下文地址\xabc\xdefx中,RE分隔符是x,第二个x代表自己,因此正则表达式是abcxdef

  2. 转义序列\n与嵌入模式空间的换行符匹配。但是,您不能在地址或替代命令中使用文字换行符。

什么这些不说,但似乎是的情况下,是在s/regex/new/命令时,regex部分是一个正则表达式,但new部分是没有的。在替换材质中,您必须使用\,然后换行以嵌入换行符。在搜索材料(regex)中,您可以使用\n

还请注意sed在线工作。默认情况下,除了正则表达式元字符$之外,模式空间末尾的换行符几乎是不可比拟的;你不能通过匹配它来简单地删除该新行。但是,您可以在模式空间中结束多行,然后您可以将嵌入换行符与\n模式匹配。

+0

谢谢,这是我第一次尝试,并没有奏效。 'sed -e's/\ n// g''问题陈述中的工作代码在搜索材料中也包含'\ n',但该部分奇怪地起作用(当它在文件的明确读取之前)。 – econ

+0

有一个问题,'sed'是基于行的,而最后一个换行符“不计数”。你不能只从行尾删除换行符;您可以删除嵌入的换行符,因为您一直在使用嵌入换行符的命令(例如,“N”和“H”)。 –

+0

它看起来像我不小心一直在引用GNU'sed'手册。我的错。我希望我明白它是怎么出错的 - 手册被大量地塞入('〜/ oss/share/man/man1/sed.1'),但即使MANPATH未设置,“man”也会设法找到它。哎呀!幸运的是,我不认为我对手册的评论是不准确的 - 正如我引用的内容不是我认为我引用的那样。当我研究出如何洗脑时,我会更新信息。 –

0

一些替代品,我倾向于在受到OSX sed特性阻碍时回落,是trperl

echo -e "foo\nbar" | tr '\n' ' ' 
foo bar 

echo -e "foo\nbar" | perl -pe 's/\n/ /' 
foo bar 
+0

谢谢,tr非常方便。对于这个问题,我非常好奇OSX的sed行为,但是你的回答可以帮助寻找任何解决方案的人。 – econ

+0

@ ghoti我同意,但它是有针对性的和建设性的 - 只是在评论中表达一下有点尴尬:-) –