Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jq: how do I update a value based on a substring match?

I've got a jq question. Given a file file.json containing:

[
  {
    "type": "A",
    "name": "name 1",
    "url": "http://domain.com/path/to/filenameA.zip"
  },
  {
    "type": "B",
    "name": "name 2",
    "url": "http://domain.com/otherpath/to/filenameB.zip"
  },
  {
    "type": "C",
    "name": "name 3",
    "url": "http://otherdomain.com/otherpath/to/filenameB.zip"
  }
]

I'm looking to create another file using jq with url modified only if the url's value matches some pattern. For example, I'd want to update any url matching the pattern:

http://otherdomain.com.*filenameB.*

to some fixed string such as:

http://yetanotherdomain.com/new/path/to/filenameC.tar.gz

with the resulting json:

[
  {
    "type": "A",
    "name": "name 1",
    "url": "http://domain.com/path/to/filenameA.zip"
  },
  {
    "type": "B",
    "name": "name 2",
    "url": "http://domain.com/otherpath/to/filenameB.zip"
  },
  {
    "type": "C",
    "name": "name 3",
    "url": "http://yetanotherdomain.com/new/path/to/filenameB.tar.gz"
  }
]

I haven't gotten far even on being able to find the url, let alone update it. This is as far as I've gotten (wrong results and doesn't help me with the update issue):

% cat file.json | jq -r '.[] | select(.url | index("filenameB")).url'
http://domain.com/otherpath/to/filenameB.zip
http://otherdomain.com/otherpath/to/filenameB.zip
%

Any ideas on how to get the path of the key that has a value matching a regex? And after that, how to update the key with some new string value? If there are multiple matches, all should be updated with the same new value.

like image 677
Steve Amerige Avatar asked Feb 07 '23 21:02

Steve Amerige


1 Answers

The good news is that there's a simple solution to the problem:

map( if .url | test("http://otherdomain.com.*filenameB.*")
     then .url |= sub(  "http://otherdomain.com.*filenameB.*"; 
           "http://yetanotherdomain.com/new/path/to/filenameC.tar.gz")
     else .
     end)

The not-so-good news is that it's not so easy to explain unless you understand the key cleverness here - the "|=" filter. There is plenty of jq documentation about it, so I'll just point out that it is similar to the += family of operators in the C family of programming languages.

Specifically, .url |= sub(A;B) is like .url = (.url|sub(A;B)). That is how the update is done "in-place".

like image 60
peak Avatar answered Feb 12 '23 10:02

peak