Почему jq’s slurp не может обрабатывать комбинацию строк здесь и других файлов?

#bash #unix #jq

Вопрос:

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

Два обычных файла:

 bash-4.2# echo '{"key":0}' > zero
bash-4.2# echo '{"key":1}' > one
bash-4.2# jq --slurp add zero one
{
  "key": 1
}
 

Обычный файл и здесь-строка (в результате появляется только обычный файл!):

 bash-4.2# jq --slurp add zero <<< '{"key":1}'
{
  "key": 0
}
 

Здесь-сначала строка, затем обычный файл (в результате появляется только обычный файл!):

 bash-4.2# jq --slurp add <<< '{"key":0,"anotherkey":2}' one
{
  "key": 1
}
 

Одиночная строка здесь (работает нормально):

 bash-4.2# jq --slurp add <<< '{"key":0}'
{
  "key": 0
}
 

Две строки здесь (работает нормально): РЕДАКТИРОВАТЬ: Вывод вводит в заблуждение, здесь происходит что-то еще.

 bash-4.2# jq --slurp add <<< '{"key":0}' <<< '{"key":1}'
{
  "key": 1
}
 

Я подозреваю, что jq работает просто отлично, и я не знаю, как bash разрешает здесь-строки. Но как бы я отладил это, чтобы улучшить свое понимание?

Примечание: Очень простым обходным путем было бы оценить мой json во время выполнения и создать файл, а затем объединить два файла, как указано выше. Я действительно хочу знать, почему приведенные выше смелые примеры не дают того, чего я ожидал.

Комментарии:

1. Обратите внимание, что herestring-это инструкция для оболочки о том, как настроить stdin. У вас может быть только один stdin и заказ wrt. то, где вы помещаете herestring среди аргументов командной строки, не меняет способа его обработки.

Ответ №1:

После прочтения комментариев это мое понимание:

<<< сначала оценивается оболочкой и перенаправляет stdin. Если jq после фильтра не получает позиционных аргументов, он считывает данные из stdin. Поэтому все эти утверждения эквивалентны:

 echo "{}" | jq --slurp add
<<< {} jq --slurp add
jq <<< {} --slurp add
jq --slurp <<< {} add
jq --slurp add <<< {}
 

Если jq получает позиционные аргументы после фильтра, он интерпретирует их как имена файлов. Он придерживается конвенции о лечении - как ЗППП.

 bash-4.2# echo '{"one":1,"two":1}' > first
bash-4.2# echo '{"three":3}' > third
bash-4.2# jq --slurp add first - third <<< '{"two":2}'
{
  "one": 1,
  "two": 2,
  "three": 3
}
 

Ответ №2:

Конструкция здесь-строка просто перенаправляет стандартный ввод. Вам отдельно нужно будет указать jq , чтобы прочитать стандартный ввод, если вы вызовете его таким образом, чтобы он получал аргументы имени файла. Де-факто стандартный способ сделать это-указать - в качестве входного (псевдо -) имени файла.

Я полагаю, что один из ваших тестовых примеров на самом деле не сработал, и просто выглядел так, как будто он сработал, потому что входные данные были сконструированы таким образом, чтобы не работать.

Комментарии:

1. Для ясности, тестовый пример, который на самом деле не сработал, является последним с двумя строками здесь. Если префикс не содержит файловый дескриптор, отличный от 0, <<< оператор перенаправляет стандартный ввод; и если команда содержит несколько перенаправлений, примененных к стандартному вводу, вступает в силу только крайнее правое. Итак, JQ получает {"key":1} , помещает его в массив и извлекает обратно; он никогда не читает {"key":0} .

2. Это помогло мне провести дальнейшее расследование. Похоже, что jq может правильно считывать данные из stdin, если не передаются никакие другие позиционные аргументы, что ввело меня в заблуждение.

Ответ №3:

Одной из идей было бы использовать подстановку процессов, которая, по сути, предоставляет jq (временный) файловый дескриптор, с которым он может работать.

Использование awk для демонстрации идеи файлового дескриптора:

 $ awk '{print FILENAME}' <(echo 'abc')
/dev/fd/63
 

Продемонстрируйте несколько своих примеров:

 $ jq --slurp add zero <(echo '{"key":1}')
{
  "key": 1
}

$ jq --slurp add zero <(echo '{"keyx":1}')
{
  "key": 0,
  "keyx": 1
}

$ jq --slurp add <(echo '{"key":0,"anotherkey":2}') one
{
  "key": 1,
  "anotherkey": 2
}

$ jq --slurp add <(echo '{"key":0}') <(echo '{"key":1}')
{
  "key": 1
}

$ jq --slurp add <(echo '{"key":0}') <(echo '{"keyx":1}')
{
  "key": 0,
  "keyx": 1
}