使用 sed(或 awk,或 tr)用表达式替换换行符 \n

Replace newline \n with expression using sed (or awk, or tr)

我正在尝试清理伪json 文件的语法。该文件太大而无法在文本编辑器 (20 gb) 中打开,因此我必须通过命令行 (运行ning Arch linux) 来完成所有这些操作。我不知道该怎么做的一件事是替换 sed 中的换行符(GNU sed v. 4.8)

具体来说,我有以下形式的数据:

{
    "id" : 1,
    "value" : 2
}
{
    "id" : 2,
    "value" : 4
}

而且我需要在闭合的大括号(但不是最后一个)后面加一个逗号。所以我希望输出看起来像:

{
    "id" : 1,
    "value" : 2
},
{
    "id" : 2,
    "value" : 4
}

理想情况下,我会在 sed 中执行此操作,但从阅读这篇文章开始,sed 首先将文本变平,因此不清楚如何替换换行符。 理想情况下,我只是 运行 类似 sed 's/}\n{/},\n{/g' test.json 的东西,但这不起作用(也没有使用 \\n 代替 \n)。

我也尝试过 awk,但是 运行 遇到了类似的问题,即无法用方括号替换硬 return 的组合。而且我可以得到 tr 来替换硬 returns,但不能替换字符组合。

有什么解决办法吗?

是的,默认情况下 sed 逐行工作。除非使用特征将多行引入模式 space,否则无法跨多行进行匹配。这是一种方法,前提是输入严格遵循所示示例:

sed '/}$/{N; s/}\n{/},\n{/}' ip.txt
  • /}$/ 匹配行尾的 }
    • {} 允许您对要针对特定​​地址执行的命令进行分组
    • N 将下一行添加到模式 space
    • s/}\n{/},\n{/ 执行所需的替换
  • 使用 -i 选项进行就地编辑

对于如下所示的序列,此解决方案可能会失败,但我假设以 } 结尾的两行不会连续出现。

}
}
{
abc
}

如果可以出现上述序列,则使用sed '/}$/{N; s/}\n{/},\n{/; P; D}'

我会按照以下方式使用 GNU AWK,令 file.txt 内容为

{
    "id" : 1,
    "value" : 2
}
{
    "id" : 2,
    "value" : 4
}

然后

awk 'BEGIN{RS="}\n{"}{printf "%s%s",sep,[=11=];sep="},\n{"}' file.txt

输出

{
    "id" : 1,
    "value" : 2
},
{
    "id" : 2,
    "value" : 4
}

说明:我使用 RS(行分隔符)在 }\n{ 上拆分,然后我不使用 ORS,因为这会导致尾随 ORS,我使用here.

描述的技巧

(在 GNU Awk 5.0.1 中测试)

使用您显示的示例,请尝试遵循 awk 程序;使用 RS 并将其值设置为 null 然后简单地应用 gsub(全局替换)来替换匹配中的 }\n{},\n{

awk -v RS= '{gsub(/}\n{/,"},\n{")} 1' Input_file

您可以使用 GNU sed for -z 来做到这一点:

$ sed -z 's/}\n{/},\n{/g' file
{
    "id" : 1,
    "value" : 2
},
{
    "id" : 2,
    "value" : 4
}

但它是不可移植的,必须一次将整个文件读入内存,如果文件格式不完全符合您的预期(例如额外的空格、注释行等),则很难适应或者您需要进行任何其他调整。

我只会使用 awk,例如在每个 Unix 机器上的任何 shell 中使用任何 awk:

awk 'NR>1{print prev (prev=="}" ? "," : "")} {prev=[=11=]} END{print prev}' file
{
    "id" : 1,
    "value" : 2
},
{
    "id" : 2,
    "value" : 4
}

这将可移植到所有 Unix 机器上,一次只读取 1 行,因此几乎不使用内存,并且可以轻松适应您输入的任何差异或您想要对输出进行的其他更改。

当最后一个}在最后一行时,可以告诉 sed跳过最后一行的替换

sed '$ !s/}/},/' test.json