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に似たシェルを使用してください。

$ 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」で始まる1つ以上の自宅の電話番号を持つ人を表す入力オブジェクトにデータを追加するには、次のようにします。

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

関連情報