如何使用 Busybox 实用程序将内容添加到文件最后一个 } 字符之前?

如何使用 Busybox 实用程序将内容添加到文件最后一个 } 字符之前?

有一个包含内容的文件

{
  "first_name": "John",
  "last_name": "Smith",
  "is_alive": true,
  "age": 27,
  "address": {
    "street_address": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postal_code": "10021-3100"
  },
  "phone_numbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ],
  "children": [
    "Catherine",
    "Thomas",
    "Trevor"
  ],
  "spouse": null
}

如何使用 Busybox 实用程序将内容添加到文件最后一个 } 字符之前,以使文件内容如下所示?

{
  "first_name": "John",
  "last_name": "Smith",
  "is_alive": true,
  "age": 27,
  "address": {
    "street_address": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postal_code": "10021-3100"
  },
  "phone_numbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ],
  "children": [
    "Catherine",
    "Thomas",
    "Trevor"
  ],
  "spouse": null,
  "field1": "value1",
  "field2": "value2",
  "field3": "value3",
  "field4": "value4"
}

但是,} 字符不一定是文件中的最后一个字符,也不一定位于最后一行。

到目前为止我只找到了这个解决方案

tac file2 | sed '0,/}/s/}/}\n"field4": "value4"\n"field3": "value3",\n"field2": "value2",\n"field1": "value1",\n,/' | tac>tmp_file && mv tmp_file file2

答案1

使用任何 awk 和任何类似 bourne 的 shell:

$ cat tst.sh
#!/usr/bin/env bash

new='\
"field1": "value1",
"field2": "value2",
"field3": "value3",
"field4": "value4"\
'

awk '
    { lines[NR] = $0 }
    $0 == "}" { last = NR - 1 }
    END {
        for ( i=1; i<last; i++ ) {
            print lines[i]
        }
        print lines[i] ","

        indent = lines[i]
        sub(/[^ \t].*/,"",indent)
        gsub(/(^|\n)/,"&"indent,new)
        print new

        for ( ++i; i<=NR; i++ ) {
            print lines[i]
        }
    }
' new="$new" file

$ ./tst.sh file
{
  "first_name": "John",
  "last_name": "Smith",
  "is_alive": true,
  "age": 27,
  "address": {
    "street_address": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postal_code": "10021-3100"
  },
  "phone_numbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ],
  "children": [
    "Catherine",
    "Thomas",
    "Trevor"
  ],
  "spouse": null,
  "field1": "value1",
  "field2": "value2",
  "field3": "value3",
  "field4": "value4"
}

请注意,它将新块的缩进设置为紧邻其之前的行的缩进,因此您不必对该缩进进行硬编码,即使后面有注释行或空白行,它也能工作。最后}一行。

答案2

使用该ed实用程序(在容器中测试BusyBox):

ed file<<EOF
$ i
  ,
  "field1": "value1",
  "field2": "value2",
  "field3": "value3",
  "field4": "value4"
.
w
q
EOF
cat file

输出文件通过测试。

答案3

如果最后一个 } 是最后一行,则可以使用 head 命令来完成。在添加的文本中包含最后一个 }:

$ cat addit
  "field1": "value1",
  "field2": "value2",
  "field3": "value3",
  "field4": "value4"
}

而你想要的是:

$ (head -n -1 input_file ;cat addit)

您从添加的文本中省略最后一个 } ,而是将上面的输出通过管道传输到此 sed:

$ sed '$ a \}'

答案4

注意:这个答案并不限于仅使用 Busybox 多二进制文件中内置的实用程序,而是使用实用jq程序。这个众所周知的 JSON 处理工具可作为大多数常见架构的静态二进制文件使用,无需升级安装或使用权限。您还可以通过公共 Docker 映像使用它。

由于 Busybox 多二进制文件可能包含数量差异很大的内置实用程序,具体取决于所使用的 Busybox(Alpine Linux 上有 302 个实用程序,但公共 Busybox Docker 映像中额外有 100 个实用程序),并且因为这不是问题中提到,我假设该限制纯粹是人为的,并不是非常重要。


安装后,jq您可以通过不同的方式使用它来添加数据,具体取决于您提供新数据的形式。

假设有单独的键和未编码的字符串,您将使用jqwith--arg key value对每个字符串进行编码并创建内部jq变量。--argjson对于已经适当编码的值(数字、布尔值、JSON 字符串或 JSON 片段),请使用--arg.然后,您可以使用表达式内以$( $key1、等)开头的键名来访问各个数据片段,或者您可以使用特殊对象来访问所有此类$var2变量:$lastnamejq$ARGS.named

jq  --arg field1 value1 \
    --arg field2 'value2 ("temporarily")' \
    --arg field3 value3 \
    --argjson field4 true \
    --argjson field5 '{ "some json fragment": [ "goes", "here" ] }' \
    '. += $ARGS.named' file.json

对于 中的第一个示例文档file.json,这将产生以下内容:

{
  "first_name": "John",
  "last_name": "Smith",
  "is_alive": true,
  "age": 27,
  "address": {
    "street_address": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postal_code": "10021-3100"
  },
  "phone_numbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ],
  "children": [
    "Catherine",
    "Thomas",
    "Trevor"
  ],
  "spouse": null,
  "field1": "value1",
  "field2": "value2 (\"temporarily\")",
  "field3": "value3",
  "field4": true,
  "field5": {
    "some json fragment": [
      "goes",
      "here"
    ]
  }
}

.显然,您可以通过指定与上面表达式不同的路径来决定将数据插入到文档中的位置。例如,要将数据添加到表示至少有一个以“212”开头的家庭电话号码的人的任何输入对象:

select(
    .phone_numbers |
    map(.type == "home" and (.number | startswith("212"))) |
    any
) += $ARGS.named

相关内容