私の目標:コマンド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
答え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.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と非常によく似ています...または少なくとも2つの間の何かです。間にはかなり直接的な翻訳があります。
処理後は次のようになります。
{
tests => {
andthis => { foo => "blabla\nblabla\n" },
commands => { value => "blabla\nblabla\n" },
includethis => "blabla\nblabla\n",
},
}
実際のjsonファイルはより多くのデータを含み、より複雑になります。
ところで、Debian では とData::Dump
をインストールでき、他のほとんどのディストリビューションでもパッケージとして使用できます。それ以外の場合はを使用してください。JSON
sudo apt install libjson-perl libdata-dump-perl
cpan
答え4
sed '/{{/!b;h
s/.*<\|>.*//g
s/.*/cat &/e
s/\n/\\n/g
x;s/{.*//
G;s/\n//
s/$/"/' file.json