JSON 문서에서 추출된 데이터를 Zsh 쉘 매개변수에 할당

JSON 문서에서 추출된 데이터를 Zsh 쉘 매개변수에 할당

일일 셸 세션에서는 JSON 문서(일부 jq 필터를 통해 추출됨)의 데이터를 Zsh 셸 매개변수(JSON 스칼라를 Zsh 스칼라로, JSON 배열을 Zsh 배열로, JSON 개체를 Zsh 연관 배열로)에 할당해야 하는 경우가 종종 있습니다. 문제(이전 질문에서 해결되지 않은 것 같음)는 데이터에 개행(또는 심지어 NUL 바이트)이 포함되는 경우가 많아 이 작업이 다소 중요하지 않다는 것입니다.

지금까지 내가 생각해낸 내용은 다음과 같습니다.

function assign-from-json {
    local -A opts && zparseopts -A opts -D -F -M -- a A && typeset -r opts
    if [[ $# -ne 3 || ( -v opts[-a] && -v opts[-A] ) ]] ; then
        >&2 printf 'Usage: %s [-a|-A] NAME FILTER JSON\n' $0
        return 2
    fi
    if [[ -v opts[-a] ]] ; then
        local -a lengths && { lengths=( "${(@f)$( jq -r "$2 | .[] | tostring | length" <<< $3 )}" ) || return $? } && typeset -r lengths
        local data && { data="$( jq -j "$2 | .[] | tostring" <<< $3 )" || return $? } && typeset -r data
        local elem
        local -a elems
        for length in "${lengths[@]}" ; do
            read -u 0 -k $length elem
            elems+=$elem
        done <<< $data
        eval "${(q)1}"='( "${elems[@]}" )'
    elif [[ -v opts[-A] ]] ; then
        local transformed_json && { transformed_json="$( jq "$2 | to_entries | map(.key, .value)" <<< $3 )" || return $? } && typeset -r transformed_json
        assign-from-json -a $1 "." $transformed_json
    else
        eval "${(q)1}"="${(q)$( jq -r $2 <<< $3 )}"
    fi
}

대부분의 경우 잘 작동합니다.

% json='
{
    "scalar": "Hello, world",
    "array": [1, 2, 3],
    "scary_scalar": "\nNewlines\u0000NUL bytes\ttabs",
    "scary_array": [
        "A\nvery\u0000scary\nvalue",
        "A less scary value",
        "eh"
    ]
}
'
% assign-from-json scalar '.scalar' $json && printf '%q\n' $scalar
Hello,\ world
% typeset -a array && assign-from-json -a array '.array' $json && printf '%q\n' "${array[@]}"
1
2
3
% assign-from-json scary_scalar '.scary_scalar' $json && printf '%q\n' $scary_scalar
$'\n'Newlines$'\0'NUL\ bytes$'\t'tabs
% typeset -a scary_array && assign-from-json -a scary_array '.scary_array' $json && printf '%q\n' "${scary_array[@]}"
A$'\n'very$'\0'scary$'\n'value
A\ less\ scary\ value
eh
% typeset -A assoc && assign-from-json -A assoc '.' $json && printf '%q -> %q\n' "${(@kv)assoc}"
array -> \[1,2,3\]
scary_array -> \[\"A\\nvery\\u0000scary\\nvalue\",\"A\ less\ scary\ value\",\"eh\"\]
scary_scalar -> $'\n'Newlines$'\0'NUL\ bytes$'\t'tabs
scalar -> Hello,\ world

불행히도 후행 개행을 처리하는 데 문제가 있는 것 같습니다.

% assign-from-json bad_scalar '.' '"foo\n"' && printf '%q\n$ $bad_scalar
foo
# expected: foo$'\n'
  1. 후행 줄 바꿈의 문제는 명령 대체를 제거하기 때문이라고 생각합니다. 쉽게 해결하는 방법이 보이시나요?
  2. assign-from-json -A assoc ...assoc이는 연관 배열을 조판하지 않고도 수행할 수 있습니다 . 이런 일이 발생하지 않도록 하려면 어떻게 해야 합니까?
  3. 코드에서 다른 문제를 발견하셨나요?

답변1

명령 대체에서 후행 개행을 유지하는 일반적인 해결 방법은 개행이 더 이상 뒤따르지 않도록 값을 추가하는 것입니다. 그런 다음 변수에서 더미 값을 제거합니다.

v1=$'line1\nline2\n\n\n'
v2=$(print $v1)
v3=$(printf $v1; print X);v3=${v3%X}
typeset -p v1 v2 v3

산출:

typeset v1=$'line1\nline2\n\n\n'
typeset v2=$'line1\nline2'
typeset v3=$'line1\nline2\n\n\n'

이는 반환 코드 처리를 복잡하게 만들기 때문에 다음과 같은 것을 원할 수도 있습니다.

local data \
  && {data="$( jq -j "$2 | .[] | tostring" <<< $3;
    ret=$?;
    print X;
    return $ret)" \
    || return $? } \
  && data=${data:%X} \
  && typeset -r data

후행 줄 바꿈을 유지하기 위한 다른 옵션은 다음에 나열되어 있습니다.이 답변. 불행히도 zsh아직 이 상황에 대한 "명령 대체 플래그"가 없다고 생각합니다 .


매개변수 확장 플래그를 t사용하여 변수 유형을 테스트하고 이름이 연관 배열을 참조하는지 아니면 다른 것을 참조하는지 확인할 수 있습니다.

function vtype {
  tt=${(Pt)1}
  if [[ $tt == association* ]]; then
      print "$1: YES [$tt]"
  else
      print "$1: no  [$tt]"
  fi
}
typeset -A a1; vtype a1
typeset -a a2; vtype a2
integer i1; vtype i1
typeset -AlxRr a3; vtype a3

위의 코드는 또한 P확장 플래그를 사용하여 위치 매개변수 값을 $1다른 매개변수 이름으로 해석합니다. ...*유형 식별자에는 t여러 구성 요소가 있을 수 있으므로 이 테스트에서는 와일드카드( )를 사용합니다 . 산출:

a1: YES [association]
a2: no  [array]
i1: no  [integer]
a3: YES [association-right_blanks-lower-readonly-export]

행운을 빌며 zsh해야 할 일을 하세요. 나는 종종 편안함의 한계를 약간 벗어난 도구가 덜 친숙한 별도의 도구를 사용하여 완전히 재구축하는 것보다 더 잘 작동한다는 것을 발견했습니다. 어려운 부분은 "약간 그 이상"을 정의하는 것입니다.

답변2

쉘(또는 심지어 zsh) 대신 Perl 또는 Python과 같은 언어를 사용해야 합니다.

예를 들어 펄을 사용하면JSON기준 치수:

$ cat json-example.pl 
#!/usr/bin/perl

use strict;
use JSON;

my $json_text;
# slurp the entire input file into a single string.
do { $/=''; $json_text=<> };

my $j = decode_json($json_text);

print $j->{scary_array}->[1], "\n";

실행 예시:

$ chmod +x json-example.pl

$ ./json-example.pl input.json 
A less scary value

Perl 데이터 구조에 대한 자세한 내용은 , perldata, perllol, perldsc매뉴얼 perlref페이지를 참조하십시오.perlreftut

json 데이터는 실제로 코드를 작성할 때나 유사한 모듈을 사용하여 디버깅을 위해 덤프할 때 Perl 데이터 구조에 매우 잘 매핑됩니다.데이터::덤퍼또는데이터::덤프, 그 표현은 json 데이터와 거의 동일합니다.

$j예를 들어 Data::Dumps dd함수를 사용하여 덤프하는 경우 다음과 같습니다.

{
  array => [1, 2, 3],
  scalar => "Hello, world",
  scary_array => ["A\nvery\0scary\nvalue", "A less scary value", "eh"],
  scary_scalar => "\nNewlines\0NUL bytes\ttabs",
}

이것은 실제로 Perl 구문을 사용하여 이 데이터를 포함하는 해시를 정의하는 방법입니다. 이를 복사하여 스크립트에 붙여넣고 변수(예 my $var = { ... };: )에 할당하면 문제 없이 컴파일됩니다.

my $var = {
  array => [1, 2, 3],
  scalar => "Hello, world",
  scary_array => ["A\nvery\0scary\nvalue", "A less scary value", "eh"],
  scary_scalar => "\nNewlines\0NUL bytes\ttabs",
};

print $var->{array}->[0], "\n";

인쇄됩니다 1.

게다가 이 데이터는 Perl에서는 전혀 나쁘지 않습니다. 이것은 단지 데이터일 뿐입니다.


모듈 참고 사항:

  • Data::DumperPerl에 포함된 핵심 Perl 모듈입니다.
  • Data::Dump별도로 설치되지 않으며 별도로 설치해야 합니다(예: apt install libdata-dump-perlDebian 등에 설치하거나 Perl을 사용하여 설치).공공 시설). Data::Dumper나는 핵심 모듈보다 그것을 선호합니다.
  • JSON핵심 Perl 모듈(Debian에서는 . 을 사용하여 설치)도 없습니다 . 컴파일된 C 코드를 사용하여 JSON 모듈의 속도를 높이는 apt install libjson-perl선택적 설치도 있습니다 .libjson-xs-perl

관련 정보