JSONドキュメントのファイル名をファイルの内容に変更します。

JSONドキュメントのファイル名をファイルの内容に変更します。

私の目標:コマンドsedまたはawk他の手段を使用してファイル名をJSONファイルの内容に変更します。

一例:

  • 変更するJSONファイル(file.json
    ...
          "value": "{{<test/myData.txt>}}"
    ...
    
    文書構造内のキーの位置ですvalue.tests[].commands[].value
  • データソースファイル(test/myData.txt
    blabla
    blabla
    
  • 希望の結果(result.json
    ...
          "value": "blabla\nblabla"
    ...
    

私の質問: 私は前に試しましたsed

sed -E "s/\{\{<([^>]+)>\}\}/{r \1}/" file.json > result.json

ただし、ファイルを読み取ることができず、次のような結果が得られます。

...
  "value": "{r test/myData.txt}"
...

私の問題を解決するためのアイデア(またはより良いアイデア)がありますかsed


解決策:

とても感謝しています!すべての答えは役に立ちますが、新しいツールをインストールせずにデフォルトの環境でGitHub actionsコマンドを使用したいと思います。だから基本的にインストールされているsedとjqの中から選択します。 sed は json ドキュメントの生の文字列の自動変換を扱わないので、論理的に jq を使用することをお勧めします。

私は使うJQプレイJQスクリプトのデバッグ。

最終スクリプトは次のとおりです。

#!/bin/bash

if [ $# -eq 0 ]; then
    printf "Utilization:\n"
    printf "$0 <FILE_INPUT> [[--output|-o] <FILE_OUTPUT>]\n"
    printf "example : ./importFile.sh test/testImportFile.side -o aeff.side"
    exit 1
fi

while [ $# -gt 0 ]; do
  case $1 in
     --output|-o)
       output="${2}"
       shift
       ;;
    *)
       input="${1}"
  esac
    shift
done

cp -p $input $output

 while : ; do
    cp -p $output "$output.tmp"
    datafile=$(jq -r 'first(.tests[].commands[].value | select(startswith("{{<"))| select(endswith(">}}"))  | ltrimstr("{{<") | rtrimstr(">}}"))' "$output.tmp")
    #echo "datafile $datafile"
    if [ -z "$datafile" ]; then
        # echo NOT FOUND
        break
    elif [ -f "$datafile" ]; then
       # echo FOUND
       jq --arg data "$(cat "$datafile")" '(first(.tests[].commands[].value | select(startswith("{{<"))| select(endswith(">}}")))) |= $data' "$output.tmp" > $output
    else
        printf 'Could not find "%s" referenced by "%s"\n' "$datafile" $input >&2
        exit 1
    fi
done
rm "$output.tmp"
echo DONE

このスクリプトを含むプロジェクトはgithubにあります。

答え1

sedドキュメントを解析し、JSONファイルに保存されているパス名(一部のJSONエンコーディング文字を含めることができます)をデコードし、JSONドキュメントに含めるファイルの内容をエンコードする必要があるため、これは問題です。そうしてこそ実現可能を使用するsedことは、.NETでJSONパーサーを実装する必要があることを意味しますsed

既存のJSON認識ツールを試してみましょう。例えばjq

質問に多くのファイルが表示されないため、ファイルが次のようになるとします。

{
  "description": "hello world example",
  "value": "{{<test/myData.txt>}}"
}

またはそれに対応する

{"description":"hello world example","value":"{{<test/myData.txt>}}"}

つまり、valueキーはJSONファイルの最上位キーの1つです。

ここでやるべきことは、valueとの間のキー値を解析し、値全体を残りのパス名に対応するファイル値に置き換えることです。{{<>}}

jqパス名を使用できます

jq -r '.value | ltrimstr("{{<") | rtrimstr(">}}")' file.json

これにより側面が削除され、{{<復号>}}化された文字列値が返されます。

この文字列を次のようにシェル変数に入れることができます。

datafile=$( jq -r '.value | ltrimstr("{{<") | rtrimstr(">}}")' file.json )

あるいはjq、シェルで評価されるディレクティブを作成することもできます(これにより、パス名が改行文字で終わることがあります)。

eval "$( jq -r '.value | ltrimstr("{{<") | rtrimstr(">}}") | @sh "datafile=\(.)"' file.json )"

この@sh演算子は、JSONファイルで解析された値がシェルに安全に引用されることを保証します。私のサンプルJSONドキュメントの場合、これはeval文字列ですdatafile='test/myData.txt'

次に、ファイルデータをインポートし、元のファイルの適切なキー値を更新します。

jq --arg data "$(cat "$datafile")" '.value |= $data' file.json

これにより、ファイルのJSONエンコードデータを含む変数が生成されますjq。データはキー値を$data更新するために使用されます。value

test/myData.txt私の小さなサンプルファイルとあなたのサンプルファイルの結果を見ると、次のようになります。

{
  "description": "hello world example",
  "value": "blabla\nblabla"
}

必要に応じて新しいファイル名にリダイレクトします。

要約:

datafile=$( jq -r '.value | ltrimstr("{{<") | rtrimstr(">}}")' file.json )
jq --arg data "$(cat "$datafile")" '.value |= $data' file.json >result.json

完全性チェックと診断メッセージを追加します。

datafile=$( jq -r '.value | ltrimstr("{{<") | rtrimstr(">}}")' file.json )

if [ -f "$datafile" ]; then
    jq --arg data "$(cat "$datafile")" '.value |= $data' file.json >result.json
else
    printf 'Could not find "%s" referenced by "%s"\n' "$datafile" file.json >&2
fi

答え2

これを使用してjsonデータを処理するpythonモジュールがあります。json

python3 -c 'import re, sys, json
jfile,outfile = sys.argv[1:]
regex,rs = re.compile(r"^\{\{<.*>\}\}$"),"\n"

with open(jfile) as f:
  d = json.load(f)

for el in d["tests"]:
  for lod in el["commands"]:
    if re.search(regex,lod["value"]):
      txtfile = re.sub(r"^\{\{<|>\}\}$","",lod["value"])
      with open(txtfile) as t:
        contents = "".join(t.readlines()).rstrip(rs)
        break
  else:
    continue
  break

for el in d["tests"]:
  for lod in el["commands"]:
    lod["value"] = contents

with open(outfile,"w") as w:
  json.dump(d,w,indent=2)
' file.json result.json

答え3

以下は非常に基本的なPerlの例です。JSONライブラリモジュール。

スクリプトは再帰的に繰り返されます。みんなjsonデータのキーは、ファイルと一致するすべてのキーを正規表現()で置き換え、\{\{<([^>]*)>\}\}/そのキーの値をファイルの内容で置き換えます。

#!/usr/bin/perl

use strict;
use JSON;
use Data::Dump qw(dd);

local $/; # read entire files at once

my $text = <>;  # slurp file.json into $text

my $json = JSON->new->canonical;  # canonical causes the keys to be sorted
my $j = $json->decode($text);
#dd $j;

process_includes($j);
#dd $j;

print $json->pretty->encode($j);


sub process_includes {
  # This subroutine recursively iterates through all the
  # keys, replacing values which match {{<filename>}}
  # with the contents of "filename".

  my $h = shift;   # expects a hashref containing json data

  foreach my $key (keys %$h) {

    if ($h->{$key} =~ m/\{\{<([^>]*)>\}\}/) {
      # we have a match, slurp in the file and apply it.

      my $file = $1;

      # read the file
      open(my $fh,"<",$file) or die "couldn't open '$file': $!\n";
      my $contents = <$fh>;
      close($fh);

      # replace the value with the file contents
      $h->{$key} = $contents;

    } elsif (ref($h->{$key}) eq "HASH") {

      # we have a hashref, so recurse into it.
      process_includes($h->{$key});
    };
  }
}

たとえば、次のように保存しjson-include.plchmod +x json-include.pl実行可能にします。

$ ./json-include.pl file2.json 
{
   "tests" : {
      "andthis" : {
         "foo" : "blabla\nblabla\n"
      },
      "commands" : {
         "value" : "blabla\nblabla\n"
      },
      "includethis" : "blabla\nblabla\n"
   }
}

file2.json含む:

$ cat file2.json 
{
   "tests" : {
      "commands" : {
         "value" : "{{<test/myData.txt>}}"
      },
      "includethis" : "{{<test/myData.txt>}}",
      "andthis" : {
         "foo" : "{{<test/myData.txt>}}"
      }
   }
}

注:上記では毎回同じファイル名を使用しますが、有効なデータが含まれている限り、必要なファイル名を使用できます。ファイル名は絶対パス名でも、現在のディレクトリへの相対パス名でもかまいません。


パールを使用できますデータ::ダンプ$jモジュールは、デコード後に正しい形式のダンプを使用して、jsonデータがPerlオブジェクトとしてどのように見えるかを示します。これにより、使用したいキーを見つけやすくなります(デバッグにも役立ちます)。コードにコメントされた例を残しました。

上記のfile2.jsonの場合、出力は今後処理はprocess_includes()次のとおりです。

{
  tests => {
    andthis     => { foo => "{{<test/myData.txt>}}" },
    commands    => { value => "{{<test/myData.txt>}}" },
    includethis => "{{<test/myData.txt>}}",
  },
}

ところで、明らかにこれはjsonデータファイルとまったく似ていません。 perl Hashes-of-Hashes(HoH、Perlデータ構造の詳細と参照man perldscman perlreftutはjsonと非常によく似ています...または少なくとも2つの間の何かです。間にはかなり直接的な翻訳があります。

処理後は次のようになります。

{
  tests => {
    andthis     => { foo => "blabla\nblabla\n" },
    commands    => { value => "blabla\nblabla\n" },
    includethis => "blabla\nblabla\n",
  },
}

実際のjsonファイルはより多くのデータを含み、より複雑になります。


ところで、Debian では とData::Dumpをインストールでき、他のほとんどのディストリビューションでもパッケージとして使用できます。それ以外の場合はを使用してください。JSONsudo apt install libjson-perl libdata-dump-perlcpan

答え4

sed '/{{/!b;h
s/.*<\|>.*//g
s/.*/cat &/e
s/\n/\\n/g
x;s/{.*//
G;s/\n//
s/$/"/' file.json

関連情報