我知道这个问题已被提出并以不同的方式回答。但我的重点是为什么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不起作用!
答案 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
在第一行附加第二行,如果该行以所需字符串开头,则删除前面的换行符并重复。否则打印第一行,删除它并重复。