如何使用tr(1)删除非空行中的换行符('\n', 0x0A)?

7
我有一个名为file1的文件,其内容如下:
The answer t
o your question 

A conclusive a
nswer isn’t al
ways possible.

When in doubt, ask pe
ople to cite their so
urces, or to explain

Even if we don’t agre
e with you, or tell y
ou.

我想将文件1转换成文件2,后者应该是这样的:
The answer to your question

A conclusive answer isn’t always possible.

When in doubt, ask people to cite their sources, or to explain

Even if we don’t agree with you, or tell you.

如果我只是执行cat file1 | tr -d "\n" > file2,那么所有的换行符都将被删除。如何仅使用tr(1)实用程序删除非空行上的换行符
6个回答

9
perl -00 -lpe 'tr/\n//d'

-00 是 Perl 的“段落”模式,使用一个或多个空行作为分隔符读取输入。 -l 将系统换行符附加到打印命令中,因此可以安全地删除输入中的所有换行符。


4

tr 无法实现此功能,但是 sed 可以轻松实现。

sed -ne '$!H;/^$/{x;s/\n//g;G;p;d;}' file1 > file2

这个程序会找到非空行并保留它们。然后,在空行上,它会删除所保存数据中的换行符,并输出结果,最后加上一个换行符。然后,这个程序将删除所保存数据,并重复此过程。
补充说明:
根据@potong的评论,这是一个不需要在文件末尾添加额外空白行的版本。
sed -ne 'H;/^$/{x;s/\n//g;G;p;};${x;s/\n//g;x;g;p;}' file1 > file2

这个不会打印最后一行。 - potong
@potong:对我来说可以。你使用的sed版本是什么? - sorpigal
你的解决方案只会在遇到空行 /^$/ 时处理保留空间,如果最后一行不是空行,则不会处理它。 - potong

2
“file1”中的换行符分为四类:
  1. 一个换行符后面跟着另一个换行符
  2. 一个换行符前面有一个换行符
  3. 文件末尾的换行符
  4. 夹在中间的换行符
通过读取整个输入(使用“-000”选项)并替换我们看到的每对换行符之间的一个换行符(“s/\n\n/\n/g”),可以删除第一类。得到的结果是:
$ perl -000 -pe 's/\n\n/\n/g' file1 
The answer t
o your question 
A conclusive a
nswer isn’t al
ways possible.
When in doubt, ask pe
ople to cite their so
urces, or to explain
Even if we don’t agre
e with you, or tell y
ou.
但这不是我们想要的,因为第一类换行符应该终止“file2”中的行。
我们可以尝试聪明地使用负向回顾来删除前面有其他换行符的换行符(第二类),但输出与前一种情况无法区分,这是有道理的,因为这次我们删除的是每对相邻换行符中的后者而不是前者。
中译英:
$ perl -000 -pe 's/(?<=\n)\n//g' file1 
你的问题的答案并不总是确定的。当有疑问时,可以要求人们引用他们的来源或者解释。即使我们不同意你的观点,也会告诉你。

即便如此,这还不是我们想要的结果,因为在file2中,前面有换行符的换行符变成了空行。

很明显,我们想保留file1末尾的换行符。

那么我们需要的程序只删除第四类情况:既不是前面有换行符,又不是后面跟着另一个换行符或逻辑输入结束符的每个换行符。

使用Perl的环视断言,规范很直接,尽管外观可能有点令人生畏。 "不是由换行符前导" 是负向后顾 (?<!\n)。使用负向前瞻(?!...),我们不想看到另一个换行符或 (|) 输入的结尾 ($)。

将所有内容放在一起,我们得到
$ perl -000 -pe 's/(?<!\n)\n(?!\n|$)//g' file1 
你问题的答案
并非总能得出确定的答案。
如果不确定,请要求人们引用他们的来源或解释。
即使我们不同意您,也不会告诉您。
最后,要创建file2,请重定向标准输出。
perl -000 -pe 's/(?<!\n)\n(?!\n|$)//g' file1  >file2

2
如果你知道输入的字符中不会出现某个特定字符,你可以像这样操作:
# Assume that the input doesn't contain the '|' character at all
tr '\n' '|' < file1 | sed 's/\([^|]\)|\([^|]\)/\1\2/g' | tr '|' '\n' > file2

这将所有换行符替换为替换字符|; 然后,sed删除在某些其他字符之后和之前出现的所有|实例; 最后,它将|替换回换行符。


2
这可能对你有用:
# sed '1{h;d};H;${x;s/\([^\n]\)\n\([^\n]\)/\1\2/g;p};d' file

The answer to your question 

A conclusive answer isn't always possible.

When in doubt, ask people to cite their sources, or to explain

Even if we don't agree with you, or tell you.

0

你不能仅使用tr来实现这个。虽然tr非常方便,但它严格是一个逐字符过滤器,没有前瞻或后顾。

你可能可以使用sed来获得你的示例输出,但这会非常痛苦(我认为!)。编辑(sed大师@Sorpigal证明了我错了!)

这里有一个使用awk的解决方案

/home/shellter:>cat <<-EOS \
| awk 'BEGIN{RS="\n\n"}; { gsub("\n", "", $0) ;printf("%s %s", $0, "\n\n") }'
The answer t
o your question 

A conclusive a
nswer isn’t al
ways possible.

When in doubt, ask pe
ople to cite their so
urces, or to explain

Even if we don’t agre
e with you, or tell y
ou.
EOS


# output
The answer to your question

A conclusive answer isnt always possible.

When in doubt, ask people to cite their sources, or to explain

Even if we dont agree with you, or tell you.

奇怪,它显示为三倍行距,但实际上是双倍行距。

Awk有预定义的变量,对于每个文件和它读取的每一行文本,都会填充这些变量,例如:

RS = RecordSeperator -- normally a line of data, but a configurable value, that when set 
                     to '\n\n' means a blank line, or a typical separation on a paragraph

$0 = complete line of text (as defined by the internal variables RS (RecordSeparator)
                             In this problem, it is each paragraph of data, viewed though
                             as a record.

$1 = first field in text (as defined by the internal variables FS (FieldSeparator)
                           which defaults to (possibly multiple) space chars OR tab char
                          a line with 2 connected spaces chars and 1 tab char has 3 fields)

NF = Number(of)Fields in current line of data (again fields defined by value of FS as 
                                                described above)

(there are many others, besides, $0, $n, $NF, $FS, $RS).

你可以通过使用变量(如代码示例中的 $i,其中 i 是一个介于 2 和 NF 之间的数字)来进行像 $1、$2、$3 这样的值的编程自增。前导的 “$” 表示给我字段 i 的值(即 $2、$3、$4……)。

希望这可以帮助到你。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接