【问题标题】:How do I delete newlines ('\n', 0x0A) from non-empty lines using tr(1)?如何使用 tr(1) 从非空行中删除换行符 ('\n', 0x0A)?
【发布时间】:2011-12-31 23:18:03
【问题描述】:

我有一个名为 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.

我想把 file1 转换成 file2。后者应如下所示:

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) 实用程序仅删除那些位于非空行上的 换行符 字符?

【问题讨论】:

    标签: perl bash sed tr


    【解决方案1】:
    perl -00 -lpe 'tr/\n//d'
    

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

    【讨论】:

      【解决方案2】:

      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:对我有用。您使用的是什么版本的 sed?
      • 您的解决方案仅在遇到空行/^$/ 时处理保留空间,如果最后一行不是空行,则不会处理它。
      【解决方案3】:

      如果您知道输入中没有出现的字符,您可以执行以下操作:

      # Assume that the input doesn't contain the '|' character at all
      tr '\n' '|' < file1 | sed 's/\([^|]\)|\([^|]\)/\1\2/g' | tr '|' '\n' > file2
      

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

      【讨论】:

        【解决方案4】:

        这可能对你有用:

        # 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.
        

        【讨论】:

          【解决方案5】:

          file1 中的换行符分为四类:

          1. 换行后跟另一个换行
          2. 换行前有换行
          3. 文件末尾的换行符
          4. 夹在中间的换行符

          通过读取整个输入删除第一个类(-000 选项)并在我们看到一对它们的任何地方替换一个换行符(s/\n\n/\n/g)得到我们

          $ perl -000 -pe 's/\n\n/\n/g' file1
          答案
          你的问题
          一个结论性的
          nswer 不是人
          可能的方式。
          如有疑问,请询问 pe
          可以引用他们的
          urces,或解释
          即使我们不同意
          和你在一起,或者告诉你
          哦。

          这不是我们想要的,因为第一类换行符应该终止 file2 中的行。

          我们可能会尝试更聪明,并使用负回溯来删除其他换行符之前的换行符(第二类),但输出与前一种情况没有区别,这是有道理的,因为这次我们删除的是后者而不是每对相邻换行符中的前者。

          $ perl -000 -pe 's/(?
          
          

          即便如此,这仍然不是我们想要的,因为其他换行符之前的换行符成为file2 中的空白行。

          很明显,我们希望在file1 末尾保留换行符。

          那么我们想要的是一个只删除第四个类的程序:每个换行符之前没有另一个换行符,并且后面既没有另一个换行符也没有逻辑输入结束。

          使用Perl's look-around assertions,规范很简单,虽然在外观上可能有点吓人。 “前面没有换行符”是负面的后视(?&lt;!\n)。使用负前瞻(?!...),我们不想看到另一个换行符或(|)输入的结尾($)。

          把它们放在一起,我们得到了

          $ perl -000 -pe 's/(?
          
          

          最后,要创建file2,重定向标准输出。

          perl -000 -pe 's/(?file2

          【讨论】:

            【解决方案6】:

            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.
            

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

            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).
            

            您可以通过使用示例代码中的变量以编程方式递增 $1、$2、$3 等值,例如 $i(i 是一个数字介于 2 和 NF 之间的变量。前导“$” 说给我字段 i 的值(即 $2、$3、$4 ...)

            我希望这会有所帮助。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-07-06
              • 1970-01-01
              • 1970-01-01
              • 2015-05-20
              • 2014-12-01
              相关资源
              最近更新 更多