使用'sed'删除特定字符的换行符

时间:2017-06-25 07:50:53

标签: awk sed

我知道这个问题已被提出并以不同的方式回答。但我的重点是为什么sed没有像我期望的那样表现出来。

对于给定的threaddump文件,我需要在每行“Locked ownable synchronizers”之前删除换行符,如下所示。

        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不起作用!

3 个答案:

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

注意:

如果您想要删除回车\r(U + 000D)而不是换行符\n(U + 000A),您应该能够替换\n in上面的代码是\r

答案 1 :(得分:1)

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进行就地编辑和其他语法糖。

答案 2 :(得分:0)

这可能适合你(GNU sed):

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

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