【问题标题】:Using 'sed' to remove newlines for specific characters使用 'sed' 删除特定字符的换行符
【发布时间】:2017-06-25 07:50:52
【问题描述】:

我知道这个问题已经以不同的方式被提出和回答。但我的重点是为什么 sed 的行为不像我期望的 vi 那样。

对于给定的线程转储文件,我需要在“锁定可拥有同步器”的每一行之前删除换行符,如下所示。

        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
    at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
    at com.project.tools.threads.NamedThread.run(NamedThread.java:37)

   Locked ownable synchronizers:
        - None

我可以使用 vi 做到这一点:

:g/^M   Locked ownable synchronizers/s//   Locked ownable synchronizers/g

^^^ ^M 是 ctrl-M。上面的 vi 命令有效,即它成功地删除了 Locked 之前的换行符。但是,当我尝试在 sed 中使用它时,以下都不起作用(我尝试了多种方法来表示换行符,但都没有奏效)。

sed -i'' -e 's/^M   Locked ownable synchronizers/   Locked ownable synchronizers/g' file.threaddump
sed -i'' -e 's/\n   Locked ownable synchronizers/   Locked ownable synchronizers/g' file.threaddump
sed -i'' -e 's/\r   Locked ownable synchronizers/   Locked ownable synchronizers/g' file.threaddump
sed -i'' -e 's/\r\n   Locked ownable synchronizers/   Locked ownable synchronizers/g' file.threaddump

据我了解,vi 命令在 sed 中工作(而且它们一直是)。这个怎么不行啊???

谢谢

PS:有效的解决方案是使用 perl:

perl -0pe 's/\n   Locked ownable synchronizers:/   Locked ownable synchronizers:/g' < file.threaddump

但我想弄清楚为什么 sed 不起作用!

【问题讨论】:

    标签: awk sed


    【解决方案1】:

    首先,在 vim 中工作的更简单的ex 命令是:

    :%s/\n\(   Locked\)/\1/
    

    现在,用 sed 替换换行符并不简单,因为 sed 逐行读取其输入,并且,对于 sed,一行本身不包含换行符(换行符只是行之间的分隔符)。因此,包含\n 的 sed 模式默认不会匹配任何内容。

    以前有人问过用sed替换换行符的问题,我们可以为您的情况采用this answer

    sed -e :a -e N -e '$!ba' -e 's/\n\(   Locked\)/\1/' file.threaddump
    

    对于这样一个简单的任务,上述解决方案相当复杂,因为它试图让 sed 做一些它不应该做的事情,即匹配换行符。

    故事的寓意是:为工作选择正确的工具。

    在这种情况下,更好的工具是允许重新定义记录分隔符,以便换行符在字符串中显示为普通字符,而不被视为记录分隔符。

    Sed 不允许这样做,因为它专门设计用于处理 ,并且换行符被硬编码作为记录分隔符。

    但是,正如您已经看到的,Perl 允许使用 -0 开关来做到这一点:

    perl -0 -p -e 's/\n(   Locked)/$1/' file.threaddump
    

    -0 开关(不带参数)基本上将记录分隔符设置为空字符串,这导致将整个输入视为单个记录。然后,您可以像 s/// 命令中的任何其他字符一样匹配换行符 \n


    注意:

    如果您想删除回车符\r (U+000D) 而不是换行符\n (U+000A),您应该可以将上面代码中的\n 替换为\r .

    【讨论】:

      【解决方案2】:

      sed 一次读取和操作 1 行。行由换行符分隔。因此,您不能从 sed 正在操作的行中删除换行符,因为它不包含换行符。

      由于 sed 仅用于在单个行上进行简单替换,因此无论如何您都应该使用 awk:

      awk -i inplace -v RS='^$' '{print gensub(/\n(\s*Locked ownable synchronizers)/,"\\1","g")}' file.threaddump
      

      上面使用 GNU awk 而不是 GNU sed 进行就地编辑和其他语法糖。

      【讨论】:

      • 你的解释很有道理,我对sed有了更好的理解。谢谢!
      【解决方案3】:

      这可能对你有用(GNU sed):

      sed -i ':a;N;s/\n\(\s*Locked ownable synchronizers:\)/\1/;ta;P;D' file
      

      在第一行添加第二行,如果该行以所需的字符串开头,则删除前面的换行符并重复。否则打印第一行,删除并重复。

      【讨论】:

        猜你喜欢
        • 2011-12-21
        • 1970-01-01
        • 2012-03-21
        • 2018-01-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-07
        • 1970-01-01
        相关资源
        最近更新 更多