jq: как обновить значение на основе совпадения подстроки?

У меня есть вопрос jq. Дан файл file.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://otherdomain.com/otherpath/to/filenameB.zip"
  }
]

Я хочу создать другой файл, используя jq с измененным URL-адресом, только если значение URL-адреса соответствует некоторому шаблону. Например, я хотел бы обновить любой URL-адрес, соответствующий шаблону:

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

в некоторую фиксированную строку, такую ​​как:

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

с полученным 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"
  }
]

Я так и не смог найти URL-адрес, не говоря уже об его обновлении. Это то, что я получил (неправильные результаты и не помогают мне с проблемой обновления):

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

Любые идеи о том, как получить путь к ключу, который имеет значение, соответствующее регулярному выражению? И как после этого обновить ключ каким-нибудь новым строковым значением? Если есть несколько совпадений, все они должны быть обновлены одним и тем же новым значением.


person Steve Amerige    schedule 29.01.2016    source источник


Ответы (2)


Хорошая новость заключается в том, что есть простое решение проблемы:

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)

Плохая новость заключается в том, что это не так просто объяснить, если вы не понимаете ключевую хитрость здесь - фильтр "|=". Об этом есть много документации jq, поэтому я просто укажу, что это похоже на семейство операторов += в языках программирования семейства C.

В частности, .url |= sub(A;B) похоже на .url = (.url|sub(A;B)). Таким образом, обновление выполняется «на месте».

person peak    schedule 29.01.2016
comment
Спасибо за обновленную часть ответа. Оно работает. Как получить путь к найденному элементу? Мне может понадобиться это, если я хочу сохранить путь, чтобы позже в сценарии bash я мог использовать этот путь для обновления этого конкретного элемента. На самом деле мне нужны две отдельные операции: найти путь к элементу, соответствующему шаблону, и обновить элемент новым значением. - person Steve Amerige; 04.02.2016
comment
@SteveAmerige - я не совсем уверен, какой у вас дополнительный вопрос (или вопросы), но вы можете прочитать .. или, возможно, задать отдельный вопрос SO. - person peak; 04.02.2016
comment
Спасибо, я так и сделаю. - person Steve Amerige; 03.03.2016
comment
Мне нравятся оба ответа в образовательных целях, и я проголосовал за них обоих. Из-за краткости я отмечаю это как принятый ответ. Кстати, извините за задержку с возвращением к этому. Ваши усилия заслуживают более своевременного ответа! - person Steve Amerige; 07.08.2017

Вот решение, которое идентифицирует пути к элементам URL с помощью tostream и select, а затем обновляет значения с помощью reduce и setpath

  "http://otherdomain.com.*filenameB.*"                      as $from
| "http://yetanotherdomain.com/new/path/to/filenameC.tar.gz" as $to

| reduce (tostream | select(length == 2 and .[0][-1] == "url")) as $p (
      .
    ; setpath($p[0]; $p[1] | sub($from; $to))
  )
person jq170727    schedule 05.08.2017
comment
Великолепно! Большое спасибо за помощь! - person Steve Amerige; 07.08.2017