文字列を見つけて、2つの区切り文字の間のすべての内容を削除します。

文字列を見つけて、2つの区切り文字の間のすべての内容を削除します。

私は検索しましたが、何が間違っているのかわかりませんが、この質問に対する答えが見つかりませんでした。

すべてのテキストが1行で保存されるファイルがあります。パターンを見つけて、区切り文字までそのテキストの前後のすべてのテキストを削除する必要があります。

前任者。文書

[{"something":false,"more":"123","moresamerecord":"otherstuff"},{"something":false,"more":"abc","moresamerecord":"otherstuff"},{"something2":false,"more":"def","moresamerecord":"otherstuff"},{"something2":false,"more":"456","moresamerecord":"otherstuff"}]

これは、複数のレコードを含む単一行であることを覚えておいてください。 "abc"を見つけて、前のレコードと次のレコードの間のすべてのエントリを削除しようとしています。

予想される結果は次のとおりです。

[{"something":false,"more":"123","moresamerecord":"otherstuff"},{"something2":false,"more":"def","moresamerecord":"otherstuff"},{"something2":false,"more":"456","moresamerecord":"otherstuff"}]

私は努力しましたが、これを理解することはできません。どんな助けでも大変感謝します。

答え1

すでに指摘したように、jqこのタイプのデータのためのツールがあります。ただし、jqは「オブジェクトのリストは角かっこで表される配列になければなりません」などの特定の構文制約を適用します。

ファイルがすでに有効なjsonであることを確認できない場合は、sedを使用して前処理することができます(結果をより簡単に表示し、精度を確認するため、jqを介して初期実行を実行します)。

$ sed 's/^/[/; s/,$/]/' data.txt | jq -r '.[]'
{
  "something": false,
  "more": "123",
  "moresamerecord": "otherstuff"
}
{
  "something": false,
  "more": "abc",
  "moresamerecord": "otherstuff"
}
{
  "something2": false,
  "more": "def",
  "moresamerecord": "otherstuff"
}
{
  "something2": false,
  "more": "456",
  "moresamerecord": "otherstuff"
}

それでは、一致するオブジェクトを削除するようにjqコマンドを変更してみましょう"more": "abc"

$ sed 's/^/[/; s/,$/]/' data.txt | jq -r '.[] | select(.more != "abc")'
{
  "something": false,
  "more": "123",
  "moresamerecord": "otherstuff"
}
{
  "something2": false,
  "more": "def",
  "moresamerecord": "otherstuff"
}
{
  "something2": false,
  "more": "456",
  "moresamerecord": "otherstuff"
}

最後に、スペースなしでカンマ区切り文字を使用して1行に再圧縮するには、後処理ステップも必要になるようです。

$ sed 's/^/[/; s/,$/]/' data.txt | jq -r '.[] | select(.more != "abc")' | sed 's/}$/},/' | tr -d ' \n'
{"something":false,"more":"123","moresamerecord":"otherstuff"},{"something2":false,"more":"def","moresamerecord":"otherstuff"},{"something2":false,"more":"456","moresamerecord":"otherstuff"},

答え2

基本的なアイデアは、パターンを区切り文字ではなく区切り文字まで拡張することです。

したがって、最も近い一致から始めるには、以外の{文字が続くもの"abc"を探します。同様に、「}」の後にない文字を見つけて、最も近い後続の文字に展開できます。{{"abc"}}

その後、カンマを処理するいくつかの極端なケースがあります。

sed 's/{[^{]*"abc"[^}]*}//;s/,,/,;s/,$//;s/^,//'

データが表示されているよりも複雑な場合、特におよび{}入れ子になる可能性がある場合は、解析に切り替えることができます。正規表現は「カウントできません」。したがって、特定の有限深さ(例:3)を処理するパターンを作成できますが、任意の深さは処理できません。

jqsedを使用する代わりに、コメントの提案を試すことは間違いなく価値があります。

答え3

jqこれが解決策でない場合は、以下をお勧めします。

# Instead of a single line pattern matching,
# make the "records" one per line
# then delete the line with the pattern
# finally get everything again to a single line
sed -e 's:,{:\n{:g;s:,$::' file | sed '/abc/d' | tr '\n' ','

ステップバイステップ:

$ sed -e 's:,{:\n{:g;s:,$::' file
{"something":false,"more":"123","moresamerecord":"otherstuff"}
{"something":false,"more":"abc","moresamerecord":"otherstuff"}
{"something2":false,"more":"def","moresamerecord":"otherstuff"}
{"something2":false,"more":"456","moresamerecord":"otherstuff"}
$ sed -e 's:,{:\n{:g;s:,$::' foo.txt | sed '/abc/d'
{"something":false,"more":"123","moresamerecord":"otherstuff"}
{"something2":false,"more":"def","moresamerecord":"otherstuff"}
{"something2":false,"more":"456","moresamerecord":"otherstuff"}
$ sed -e 's:,{:\n{:g;s:,$::' foo.txt | sed '/abc/d' | tr '\n' ','
{"something":false,"more":"123","moresamerecord":"otherstuff"},{"something2":false,"more":"def","moresamerecord":"otherstuff"},{"something2":false,"more":"456","moresamerecord":"otherstuff"},

答え4

awk '
  BEGIN { FS = "},{" }
  { k=0
    for (i=1; i<=NF; i++)
      if ($i !~ /"abc"/)
        printf "%s%s", (k++?FS:""), $i
    $0=""
  }1
' file

$ cat file \
| sed -e 's/},{/}\n{/g'           \
| sed -E '/([{:,])"abc"([,:}])/d' \
| paste -sd, -                    \
;
  • レコードを1行に分割します。
  • ここで、以下を含むすべての履歴を削除します。"abc"
  • カンマでレコードを縫う,

出力:

{"something":false,"more":"123","moresamerecord":"otherstuff"},{"something2":false,"more":"def","moresamerecord":"otherstuff"},{"something2":false,"more":"456","moresamerecord":"otherstuff"}

関連情報