【问题标题】:Replace newline \n with expression using sed (or awk, or tr)使用 sed(或 awk 或 tr)将换行符 \n 替换为表达式
【发布时间】:2021-10-25 15:44:30
【问题描述】:

我正在尝试清理伪 json 文件的语法。该文件太大而无法在文本编辑器(20 gb)中打开,所以我必须通过命令行(运行 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,但遇到了类似的问题,即无法用括号替换硬返回的组合。而且我可以用 tr 替换硬返回,但不能替换字符的组合。

关于如何解决这个问题的任何想法?

【问题讨论】:

    标签: awk sed grep tr


    【解决方案1】:

    是的,默认情况下sed 逐行工作。除非您使用功能将多行引入模式空间,否则您无法匹配多行。这是一种方法,只要输入严格遵循所示示例:

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

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

    }
    }
    {
    abc
    }
    

    如果上述顺序可能发生,请使用sed '/}$/{N; s/}\n{/},\n{/; P; D}'

    【讨论】:

    • 这很好用。需要注意的一件事是字符编码似乎也很重要。我必须将文件重新编码为 ascii 才能使其正常工作。
    • @user3037237 可能值得在unix.stackexchange.com 上询问您的编码问题(它也可能适用于适当的语言环境设置)
    【解决方案2】:

    使用您展示的示例,请尝试关注awk 程序;使用 RS 并将其值设置为 null 然后只需应用 gsub(全局替换) 在匹配项中从 }\n{ 替换为 },\n{

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

    【讨论】:

    • 如果没有段落,这对于 20 GB 文件来说不是一个好主意
    【解决方案3】:

    您可以使用 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=$0} END{print prev}' file
    {
        "id" : 1,
        "value" : 2
    },
    {
        "id" : 2,
        "value" : 4
    }
    

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

    【讨论】:

      【解决方案4】:

      我会按照下面的方式使用 GNU AWK,让 file.txt 内容成为

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

      然后

      awk 'BEGIN{RS="}\n{"}{printf "%s%s",sep,$0;sep="},\n{"}' file.txt
      

      输出

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

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

      (在 GNU Awk 5.0.1 中测试)

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          猜你喜欢
          • 2015-11-21
          • 1970-01-01
          • 2014-03-15
          • 2013-11-03
          • 2011-01-23
          • 1970-01-01
          • 2018-05-24
          • 1970-01-01
          • 2013-01-23
          相关资源
          最近更新 更多