题 如何使用sed替换换行符(\ n)?


如何更换换行符(\n)使用sed命令?

我没试成功:

sed 's#\n# #g' file
sed 's#^$# #g' file

我如何解决它?


1128
2017-08-09 19:10


起源


tr 如果为单个字符替换单个字符,它只是作业的正确工具,而上面的示例显示用空格替换换行符。所以在上面的例子中,tr可以工作..但稍后会限制。 - Mayhem
@Mayhem, sed 's/$/ NewDelim/' | tr '\n' ' '。使用 sed 将新分隔符附加到每行的末尾,然后删除换行符 tr。不那么神秘了 sed 唯一的方法,IMO。 - cp.engr
tr 在正确的工作工具中,因为提问者想用空格替换每个换行符,如他的例子中所示。替换换行是独一无二的 sed 但很容易做到 tr。这是一个常见的问题。执行正则表达式替换不是通过 tr 但是 sed,对于另一个问题,这将是正确的工具。 - Mike S
“tr”也可以删除换行符`tr -d'\ n'`但是你也可以删除返回更通用的`tr -d'\ 012 \ 015'`。 - anthony
警告:“tr”在Linux和旧Solaris机器之间的字符范围方面的行为不同(EG sol5.8)。 EG:`tr -d'a-z'`和`tr -d'[a-z]'`。为此我建议你使用没有那种差异的“sed”。 - anthony


答案:


将此解决方案与GNU一起使用 sed

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

这将在循环中读取整个文件,然后用空格替换换行符。

说明:

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

这是跨平台兼容的语法,适用于BSD和OS X. sed (按照 @Benjie评论):

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

1281
2017-08-09 20:26



@Arjan和Masi:OS X使用BSD sed 而不是GNU sed,所以两者之间可能存在一些微妙的(有些不那么微妙的)差异。如果您在OS X和* nix机器上工作,这将是一个持续的痛苦。我通常安装GNU coreutils 和 findutils 在OS X上,忽略BSD版本。 - Telemachus
该 :a 不是注册,它是分支标签。这是一个目标 b 命令*的作用类似于“goto”。将其称为寄存器意味着您可以创建存储位置。只有两个“寄存器”;一个称为“保留空间”,您的脚本未使用,另一个称为“模式空间”。该 N 命令将换行符和输入文件的下一行附加到模式空间。 [*你可以有多个标签& b 命令。如果你有 b 命令没有附加标签字符,它分支到脚本的末尾以读取下一行并再次循环。] - Dennis Williamson
您可以通过单独执行命令而不是用分号分隔来运行此跨平台(即在Mac OS X上): sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g' - Benjie
为什么没有人评论这是一个愚蠢的混乱(不是答案本身,而是提出的答案是一个非常简单的问题的最佳解决方案)。 Sed看起来像一辆通常运行良好的汽车,但如果你想开车到附近的特定街道,唯一的办法就是用直升机抬起汽车。 - Ark-kun
来吧伙计们 - 261赞成一个疯狂的,难以理解的解决方案,不起作用???? sed是一个很好的工具,可以在一行上进行简单的替换,对于任何其他只使用awk的东西。好悲伤.... - Ed Morton


使用 tr 代替?

tr '\n' ' ' < input_filename

或者完全删除换行符:

tr -d '\n' < input.txt > output.txt

或者如果你有GNU版本(有很长的选项)

tr --delete '\n' < input.txt > output.txt

1448
2017-08-09 19:16



Sed是基于行的,因此很难掌握换行符。 - Alexander Gladysh
sed在输入的“流”上工作,但它以换行符分隔的块来理解它。它是一个unix工具,这意味着它做得很好。一件事是“在文件行上工作”。让它做其他事情会很难,并且冒着错误的风险。故事的寓意是:选择合适的工具。你的很多问题似乎都采取了“我怎样才能让这个工具做一些从未打算过的事情?”的形式。这些问题很有意思,但如果他们在解决实际问题的过程中遇到问题,那么你可能做错了。 - dmckee
+1使用正确的工具进行工作。 - poindexter
tr很棒,但你只能用单个字符替换换行符。如果要使用字符串替换换行符,则需要使用其他工具 - Eddy
@Eddy - 我用tr用文本中没有出现的字符替换新行(我用反引号),然后sed用我想要使用的字符串替换反引号 - rjohnston


快速回答:

sed ':a;N;$!ba;s/\n/ /g' file
  1. :一个  创建标签'a'
  2. ñ  将下一行附加到模式空间
  3. $!  如果不是最后一行BA  分支(转到)标签'a'
  4. 小号  替代/ \ n /  新行的正则表达式//  一个空间/G  全局匹配(尽可能多次)

sed将循环执行步骤1到3,直到它到达最后一行,让所有行都适合模式空间,其中sed将替换所有\ n个字符


备择方案

所有替代品,不同于 SED 不需要到达最后一行开始该过程

庆典, 慢

while read line; do printf "%s" "$line "; done < file

perl的SED像速度一样

perl -p -e 's/\n/ /' file

TR, 比...快 SED,只能替换为一个字符

tr '\n' ' ' < file

TR像速度一样,只能用一个字符代替

paste -s -d ' ' file

AWKTR像速度一样

awk 1 ORS=' ' file

其他替代品如 “echo $(<file)” 很慢,只适用于小文件,需要处理整个文件才能开始这个过程。


答案很长 来自 sed FAQ 5.10

5.10。为什么我不能使用\ n转义匹配或删除换行符
      序列?为什么我不能使用\ n匹配2行或更多行?

\ n将永远不会匹配行尾的换行符,因为
   在将线放入线之前,始终会删除换行符
   模式空间。要在模式空间中获得2行或更多行,请使用
   'N'命令或类似的东西(例如'H; ...; g;')。

Sed的工作原理如下:sed一次读取一行,然后删除
   终止换行符,将剩下的内容放入模式空间中
   sed脚本可以解决或更改它,以及模式空间
   打印后,在stdout(或文件)中附加换行符。如果
   使用'd'或'D'完全或部分删除模式空间
换行是  在这种情况下添加。因此,脚本就像

  sed 's/\n//' file       # to delete newlines from each line             
  sed 's/\n/foo\n/' file  # to add a word to the end of each line         

永远不会工作,因为删除了尾随换行符 之前
   该行被放入模式空间。要执行上述任务,
   请改用以下脚本之一:

  tr -d '\n' < file              # use tr to delete newlines              
  sed ':a;N;$!ba;s/\n//g' file   # GNU sed to delete newlines             
  sed 's/$/ foo/' file           # add "foo" to end of each line          

由于除了GNU sed之外的sed版本对大小有限制
   在模式缓冲区中,Unix'tr'实用程序在这里是首选。
   如果文件的最后一行包含换行符,则GNU sed将添加
   输出的换行但删除所有其他,而tr将
   删除所有换行符。

要匹配两行或更多行的块,有3种基本选择:
   (1)使用'N'命令将Next行添加到模式空间;
   (2)使用'H'命令至少两次追加当前行
   到保留空间,然后从保留空间检索行
   用x,g或G;或(3)使用地址范围(见上文第3.3节)
   匹配两个指定地址之间的行。

选择(1)和(2)将\ n放入模式空间,在那里
   可以根据需要进行寻址('s / ABC \ nXYZ / alphabet / g')。一个例子
   第4.13节中使用'N'删除一行
   (“如何删除一个块 具体 连续的线?“)。这个
   可以通过将delete命令更改为某些内容来修改示例
   否则,如'p'(打印),'我'(插入),'c'(更改),'a'(追加),
   或's'(替代)。

选择(3)不会将\ n放入模式空间,但它
   匹配一连串的连续线,所以可能你没有
   甚至需要\ n才能找到你想要的东西。自GNU sed
   版本3.02.80现在支持此语法:

  sed '/start/,+4d'  # to delete "start" plus the next 4 lines,           

除了传统的'/ from here /,/ to there / {...}'范围
   地址,可以完全避免使用\ n。


414
2017-10-08 14:55



tr 是一个好主意,你的整体报道是一个高质量的答案。 - New Alexandria
+1使用(标准效用) paste......以及所有其他人! - Totor
@elgalu尝试这个 unix.stackexchange.com/questions/4527/... - hdorio
关于这个答案最好的部分是“长答案”解释了命令的工作原理和原因。 - pdwalker
这可能是我在stackexchange上阅读的数千个答案中最有帮助的。我需要跨行匹配多个字符。以前没有sed示例涵盖多行和tr无法处理多个字符匹配。 Perl看起来不错,但是没有按照我的预期工作。如果可以的话,我会多次投票给这个答案。 - mightypile


一个较短的awk替代品:

awk 1 ORS=' '

说明

awk程序由条件代码块组成的规则构成,即:

condition { code-block }

如果省略代码块,则使用默认值: { print $0 }。就这样 1 被解释为真实的条件 print $0 为每一行执行。

什么时候 awk 读取输入,它根据值将其拆分为记录 RS (记录分隔符),默认情况下是换行符 awk 默认情况下会解析输入行。分裂也涉及剥离 RS 从输入记录。

现在,在打印记录时, ORS (输出记录分隔符)附加到它(默认再次是换行符)。所以通过改变 ORS在空格中,所有换行符都更改为空格。


176
2018-02-13 12:12



清晰,简洁,优雅,功能齐全! +1。 - Ed Morton
我非常喜欢这个简单的解决方案,它比其他解决方案更具可读性 - Fedir RYKHTIK
如果它更有意义,这可以有效地写成: awk 'BEGIN { ORS=" " } { print $0 } END { print "\n"} ' file.txt (添加结尾换行只是为了说明开始/结束); “1”评估为 true (处理线)和 print (打印线)。条件也可以添加到此表达式中,例如,仅处理与模式匹配的行: awk 'BEGIN { ORS=" " } /pattern/ { print $0 } END { print "\n"} ' - michael
我喜欢这种方法,应该给予更多的赞成恕我直言。 - Panagiotis Moustafellos
你可以做更多的simle: code awk'ORD =“”'file.txt code - Udi


gnu sed有一个选择 -z 对于null分隔的记录(行)。你可以打电话:

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

93
2018-05-05 09:43



即使输入确实包含空值,它们也将被保留(作为记录分隔符)。 - Toby Speight
@TobySpeight,谢谢。你是对的! - JJoao
如果没有空值,这不会加载整个输入吗?在这种情况下,处理一个数千兆字节的文件可能会崩溃。 - Ruslan
@Ruslan,是的,它加载了整个输入。对于千兆字节的文件,这个解决方案不是一个好主意。 - JJoao
这是认真的 最好 回答。其他表达式太扭曲而无法记住。 @JJoao你可以用它 -u, --unbuffered。该 man mage说:“从输入文件加载最少量的数据并更频繁地刷新输出缓冲区”。 - not2qubit


Perl的 版本按预期方式工作。

perl -i -p -e 's/\n//' file

正如评论中指出的那样,值得注意的是,这种编辑已经到位。 -i.bak 如果您的更换,将在更换前为您提供原始文件的备份 正则表达式 并不像你想的那么聪明。


77
2017-08-09 19:25



请至少提一下 -i 没有后缀使 没有备份。 -i.bak 保护你免受一个简单,丑陋的错误(比如忘记输入 -p 并将文件归零)。 - Telemachus
@Telemachus:这是一个公平的观点,但无论如何都可以争论。我没有提到它的主要原因是OP问题中的sed示例没有进行备份,所以这里似乎是多余的。另一个原因是我从未真正使用过备份功能(实际上我觉得自动备份很烦人),所以我总是忘记它在那里。第三个原因是它让我的命令行变长了四个字符。无论好坏(可能更糟),我都是一个强迫性的极简主义者;我只是喜欢简洁。我意识到你不同意。我将尽力记住将来警告备份。 - ire_and_curses
@Ire_and_curses:实际上,你只是因为无视我而做出了一个非常好的论据。也就是说,你有理由做出选择,无论我是否同意这些选择,我当然都会尊重这一点。我不确定为什么,但我最近对这个特别的事情一直在流泪(the -i Perl中没有后缀的标志)。我相信我很快就能找到别的东西来讨论。 :) - Telemachus
@ire_and_curses:我刚检查过,我没有意识到我已经烦恼了 您 特别是在过去两三天内这两次。我想是时候让我放开这个特殊问题并散步。 - Telemachus
@Telemachus:没问题。我为后代编辑了答案。希望你喜欢散步。 ;) - ire_and_curses


谁需要 sed?这里是 bash 办法:

cat test.txt |  while read line; do echo -n "$line "; done

40
2017-08-11 12:07



Upvote,我通常使用最佳答案,但是当通过它来管道/ dev / urandom时,sed将不会打印直到EOF,而^ C不是EOF。每次看到换行符时都会打印此解决方案。正是我需要的!谢谢! - Vasiliy Sharapov
那么为什么不:echo -n`cat days.txt` 从这篇文章 - Tony
@Tony因为反引号被弃用而且cat是多余的;-)使用:echo $(<days.txt) - seumasmac
甚至没有使用 cat: while read line; do echo -n "$line "; done < test.txt。如果子shell是个问题,可能会有用。 - Carlo Cannas
echo $(<file) 挤压 所有 空白到一个空格,而不仅仅是换行:这超出了OP所要求的范围。 - glenn jackman


为了使用awk替换所有换行符,而不将整个文件读入内存:

awk '{printf "%s ", $0}' inputfile

如果你想要一个最终换行符:

awk '{printf "%s ", $0} END {printf "\n"}' inputfile

您可以使用空格以外的角色:

awk '{printf "%s|", $0} END {printf "\n"}' inputfile

24
2018-03-30 06:41



这对我来说很有用 sed 解决方案没有 - New Alexandria


三件事。

  1. tr (要么 cat等等)绝对不需要。 (GNU) sed 和(GNU) awk合并后,可以完成所需文本处理的99.9%。

  2. stream!=基于行。 ed 是一个基于行的编辑器。 sed 不是。看到 sed讲座 有关差异的更多信息。大多数人都很困惑 sed 基于行,因为默认情况下,它对SIMPLE匹配的模式匹配不是很贪婪 - 例如,在进行模式搜索并替换一个或两个字符时,它默认只替换它找到的第一个匹配(除非全局命令另有规定)。如果它是基于行的而不是基于STREAM的,那么甚至不会有全局命令,因为它一次只评估行。试试跑步 ed;你会发现差异。 ed 如果你想迭代特定的行(例如在for循环中),这是非常有用的,但大多数时候你只是想要 sed

  3. 话虽如此,

    sed -e '{:q;N;s/\n/ /g;t q}' file
    

    在GNU中运行得很好 sed 版本4.2.1。上面的命令将用空格替换所有换行符。输入它很丑,有点麻烦,但它工作得很好。该 {}可以省略,因为它们只是为了理智而被包括在内。


20
2018-05-01 11:04



什么是 one?我不知道这个命令! - brandizzi
作为一个只知道足够的人 sed 要做基本的事情,我不得不说这不仅仅是关于你的事情 能够 做的 sed 而是理解发生了什么是多么容易。我很难与你合作 sed 所以当我可以使用它时,我更喜欢更简单的命令。 - Nate
运用 t q 作为条件跳转,这适用于类似的模式 s/\n / / (以连接所有以空格开头的行)而不将整个文件读入内存。转换多兆字节文件时很方便。 - textshell
您链接的文章并未反映您所说的内容 - hek2mgl
这比大输入接受的答案慢近800倍。这是因为在越来越大的输入上运行替代每一行。 - Thor


tr '\n' ' ' 

是命令。

简单易用。


18
2018-04-13 19:01



或简单地说 tr -d '\n' 如果你不想添加空格 - spuder