0

I have several .json files similar to this.

{
    "AcquisitionNumber": 1,
    "TotalReadoutTime": 0.035,
    "IntendedFor": "func/sub-02_task-rest_run-01_bold.nii.gz"
}

I want to change the sub number in the "IntendedFor" line using a bash variable, looping over different subs.For example if sub is 03:

sub=03
echo $sub
03

How can I change the value of sub-02 to sub-03 using this bash variable?

2
  • 3
    Use the JSON parser jq Commented Oct 7, 2018 at 14:16
  • A huge list of alternatives to jq here: stackoverflow.com/a/49011455/2440 Commented Oct 8, 2021 at 11:26

4 Answers 4

6

Use :

jq --arg sub "$sub" '.IntendedFor |= sub("(?<=sub-)[^_]+"; $sub)' file

See this online example

Note that your jq binary must be compiled with regex support in order to use sub function.

And, you can implement inline editing using a for loop and temporary files:

sub=03

for jsonfile in *.json; do
    tempfile=$(mktemp -u)
    jq --arg sub "$sub" '.IntendedFor|=sub("(?<=sub-)[^_]+";$sub)' "$jsonfile" > "$tempfile"
    mv "$tempfile" "$jsonfile"
done
Sign up to request clarification or add additional context in comments.

Comments

2

With jq and bash:

value=$(jq -r '.IntendedFor' file)
new_value="${value/sub-02/sub-03}"
jq --arg new "$new_value" '.IntendedFor |= $new' file

Output:

{
  "AcquisitionNumber": 1,
  "TotalReadoutTime": 0.035,
  "IntendedFor": "func/sub-03_task-rest_run-01_bold.nii.gz"
}

3 Comments

you can use jq's --arg option to pass shell variables to jq
Thanks, that makes it easier.
This does not change the original file but outputs the result to the terminal, right?
1

Using sponge (part of moreutils):

for f in *.json; do
  jq --arg sub "$sub" '.IntendedFor |= sub("/sub-[^_]+";"/sub-"+$sub)' "$f" | sponge "$f"
done

In any case, a simple regex suffices.

sponge for Windows

See:

Comments

0

There are better ways of doing this, but there is a bash-solution:

#!/bin/bash
fromstring="sub-02"
tostring="sub-03"

while read line; do
    case "$line" in
    (*IntendedFor*) echo $(line/$fromstring/$tostring) ;;
    (*)  echo $line ;;
    esac
 done < JSONfile

3 Comments

I didn't downvote, but your use of cat is a UUOc` (Unnecessary Use Of cat). Unless you are concatenating files, if you find yourself doing cat somethingelse, then it is probably a UUOc. Here using redirection, e.g. while read line; do ... done <jsonfile is the proper way of feeding the while loop. I suspect the ding comes from the fact that parsing json or xml in bash is simply unwise, there is zero format validation. That is why we have tools like jq and xmlstartlet.
As I said, "There are better ways of doing this". But I thought it to be a nice puzzle for doing it in bash only. I removed the cat from the answer, which makes it work completely in bash, without external programs.
should this have the syntax ${variable//pattern/replacement}, see stackoverflow.com/q/13043344/3208553

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.