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 파일의 최상위 키 중 하나입니다.

여기서 할 일은 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 문서의 경우 이는 evalstring 입니다 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.pl하고 chmod +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 perldsc) man perlreftut는 json과 매우 유사합니다... 또는 적어도 둘 사이의 무언가입니다. 사이에는 상당히 직접적인 번역이 있습니다.

처리 후 다음과 같습니다.

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

실제 json 파일은 더 많은 데이터를 포함하고 더 복잡해집니다.


그런데 데비안에서는 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

관련 정보