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ページperldscperlref参照してください。perlreftut

jsonデータは、実際にコードを書くときや、同様のモジュールを使用してデバッグ用にダンプするときに、Perlデータ構造に非常によくマッピングされます。データ::ダンパーまたはデータ::ダンプ、その表現はjsonデータとほぼ同じです。

$jたとえば、Data::Dumpsdd関数を使用してダンプする場合は、次のようになります。

{
  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

関連情報