我有一个输入文件,其中包含 Unix (LF) 和 Windows (CR/LF) 样式的换行符。 (具体来说,它是来自 Linux 系统的 XML,但它包含一些原始 HTTP 标头,并且 HTTP 更喜欢使用 CRLF 作为标头):
<response_page cause="default">
<response_type>custom</response_type>
<response_header>HTTP/1.1 200 OK^M
Cache-Control: no-cache^M
Pragma: no-cache^M
Connection: close</response_header>
我正在编写一个 gawk 脚本来遍历这个文件,对 XML* 进行一些简单的调整,唯一的问题是它读取 LF 和 CRLF 有效的 RS,但只输出 LF,无论那里有什么......本质上,它会删除 CR。
我尝试过各种方法,其中最雄心勃勃的是 RS 的正则表达式匹配和打印 RT:
BEGIN { RS = "\r\n|\n"; go = "no" }
(go ~ /yes/) {
sub(/false/, "true", $0)
go = "no"
}
($0 ~ /<signature signature_id="200000017">/) {
print "Found signature!"
go = "yes"
}
{
printf $0 RT
}
我非常感谢任何关于让 gawk 重现混合平台 RS 终结者的指示。
* 在这种情况下,简单的调整是将具有正确签名 ID 的行后面的行中的“false”更改为“true”。我完全意识到使用 XML 解析器是完成此任务的正确方法,但对于如此轻量级的需求,我试图避免陷入 XML 解析带来的痛苦和焦虑。
更新:
事实证明,这个解决方案在 Linux 下运行时是有效的。当在 Windows 上的 Cygwin gawk 下运行时,CRLF/LF 区别明显被静音,并且无法按预期工作。我将答案点授予Peter.O,尽管他本质上重申了我正在尝试的事情,因为他以彻底的方式这样做,当我意识到我们在做同样的事情而我的不起作用时,这让我质疑我的假设。
答案1
您可以使用内置变量RT
每次读取记录时都会设置 RT。它包含与 RS(记录分隔符)表示的文本匹配的输入文本。该变量是 gawk 扩展。
printf '%s\n' LF CRLF$'\r' |
gawk 'BEGIN { RS = "\r\n|\n" }
{ printf($0 RT) }'
通过管道传输到时的输出sed -n l
- 显示CR作为\r
, 和end-of-line
作为$
- 其中,到sed
表示下一个字符是\n
(或者end-of-input
。
LF$
CRLF\r$
但是,如果您想将终止符从 CRLF 切换到 LF 或反之亦然,则有两个操作:
printf '%s\n' was-LF was-CRLF$'\r' |
gawk 'BEGIN { RS = "\r\n|\n" }
RT == "\r\n" { printf($0 "\n") }
RT == "\n" { printf($0 "\r\n") }'
通过管道传输到时输出sed -n l
was-LF\r$
was-CRLF$
if
注意:当测试不是(主要部分)代码的第一行时, 您将需要使用它们:
gawk 'BEGIN { RS = "\r\n|\n" }
{ # some processing code here (before the tests)
if( RT == "\r\n" ) { printf($0 "\n") }
if( RT == "\n") { printf($0 "\r\n") } }'
答案2
一个简单的解决方案是仅将 LF 视为行结尾,拉出最终的 CR(如果有),然后将其打印出来。
{ CR = (sub(/\r$/,"") ? "\r" : "") }
… { … print "stuff" CR }
即使最后一个输入行未终止,输出也始终以 LF 结尾。