I have several text files, each one has a title inside. For example:
echo 'title: hello' > 1.txt
echo 'title: world' > 2.txt
echo 'title: good' > 3.txt
And I have a JSON file called abc.json generated by a shell script like this:
{
"": [
{
"title": "",
"file": "1"
},
{
"title": "",
"file": "2"
},
{
"title": "",
"file": "3"
}
]
}
What I want is to update the title value in the abc.json by the title in the respective text file, like this:
{
"": [
{
"title": "hello",
"file": "1"
},
{
"title": "world",
"file": "2"
},
{
"title": "good",
"file": "3"
}
]
}
The text files and the JSON files are in the same directory like this:
➜ tmp.uFtH6hMC ls
1.txt 2.txt 3.txt abc.json
Thank you very much!
CodePudding user response:
You can use a shell loop to iterate over your files, extract the second column, create each array element and then transform the stream of array elements into your final object:
for f in *.txt; do
cut -d' ' -f2- "$f" | jq -R --arg file "$f" '{title:.,file:($file/"."|first)}';
done | jq -s '{"":.}'
It is also possible to remove the file extension in shell directly, which makes the jq filter a little bit simpler:
for f in *.txt; do
cut -d' ' -f2 "$f" | jq -R --arg file "${f%.txt}" '{title:.,$file}';
done | jq -s '{"":.}'
CodePudding user response:
Since the .title
and .files
has the same number, we can use that to index it from the input.
So using cut
we can read all the *.txt
files, split on
and then get the second to last field, this gives:
cat *.txt | cut -d ' ' -f 1-
hello
world
good
(titles with spaces will work due to the -f 1-
)
Using --arg
we pass that to jq, which we then parse into an array:
($inputs | split("\n")) as $parsed
Now that $parsed
looks like:
[
"hello",
"world",
"good"
]
To update the value, loop over each object in the ""
array, then get the matching value from $parsed
by using .file | tonumber - 1
(since array are 0-indexed)
jq --arg inputs "$(cat *.txt | cut -d ' ' -f 1-)" \
'($inputs | split("\n")) as $parsed
| .""[]
|= (.title = $parsed[.file | tonumber - 1])' \
abc.json
Output:
{
"": [
{
"title": "hello",
"file": "1"
},
{
"title": "world",
"file": "2"
},
{
"title": "good",
"file": "3"
}
]
}
CodePudding user response:
Use input_filename
to access the filename, and you can accomplish everything in one go:
jq -Rn --argfile base abc.json '
reduce (inputs | [ltrimstr("title: "), (input_filename | rtrimstr(".txt"))])
as [$title, $file] ($base; (.[""][] | select(.file == $file)).title = $title)
' *.txt
{
"": [
{
"title": "hello",
"file": "1"
},
{
"title": "world",
"file": "2"
},
{
"title": "good",
"file": "3"
}
]
}