Почему синтаксис недопустим в этом Python однострочнике с использованием for-l oop? - PullRequest
1 голос
/ 12 марта 2020

Я работаю над Bash скриптом. В нем есть пара случаев, когда мне нужно проанализировать некоторые JSON. Мой обычный подход к этому заключается в следующем:

MY_JSON=$(some command that prints JSON to stdout)
RESULT=$(python -c "import json,sys;data=json.load(sys.stdin); python code here that prints out the value(s) I need")

Это имеет тенденцию работать хорошо. Однако вчера я столкнулся с проблемой. У меня был следующий код:

MY_JSON=$(command that returns JSON containing an array of IDs)
IDS=$(echo "${MY_JSON}" | python -c "import json,sys;data=json.load(sys.stdin); for a in data['array']: print(a['id'])")

Когда я запустил этот код, я получил "Синтаксическая ошибка" с кареткой, указывающей на f in for.

В моем поиске все, что я обнаружил, указывало на то, что когда вы получаете синтаксическую ошибку в самом первом символе оператора, это обычно означает, что вы что-то напортачили в предыдущем выражении. Тем не менее, если я удаляю для l oop полностью, я не получаю синтаксическую ошибку. Так что, очевидно, проблема в l oop.

Что я сделал не так? Как синтаксическая ошибка может быть первым символом действительного ключевого слова?

Я закончил тем, что нашел ответ, который я опубликую ниже, чтобы помочь другим, которые пытаются построить Python однострочников, включающих for l oop - но я надеюсь, что кто-нибудь сможет ответить лучше ответом, возможно, используя понимание (которое я не до конца понимаю) или что-то еще вместо для l oop так что я действительно могу выполнить это sh в одну строку. Использование языка, отличного от Python, также будет приемлемо, если оно обычно доступно на хосте Linux.

Чтобы было ясно, я бы искал решения, использующие истинный синтаксический анализ JSON, а не какое-либо приближение, используя ваш любимый инструмент для работы со строками (sed, awk, et c), который был бы * * * * * * * * * * * * * * * * * * * * *.

Ответы [ 3 ]

4 голосов
/ 12 марта 2020

Заявления в грамматике Python делятся на две группы, простые операторы и составные операторы:

stmt: simple_stmt | compound_stmt

Только простой оператор может содержать ; и простое утверждение ограничено так называемыми малыми утверждениями :

simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE

Малые выражения не включают в себя for петли.

small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
             import_stmt | global_stmt | nonlocal_stmt | assert_stmt)

A for l oop, скорее, составное утверждение:

compound_stmt: if_stmt | while_stmt | <b>for_stmt</b> | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt
2 голосов
/ 12 марта 2020

Использование языка, отличного от Python, также будет приемлемым, если оно обычно доступно на хосте Linux.

Give JQ попытка Это полноценный язык запросов с компактным синтаксисом, идеально подходящий для анализа и манипулирования в командной строке JSON.

IDS=$(jq -r '.array[].id' <<< "$MY_JSON")

Или, безопаснее, если идентификаторы могут содержать пробелы или другие специальные символы:

readarray -t IDS < <(jq -r '.array[].id' <<< "$MY_JSON")

Если бы они могли содержать переводы строк, эта супер параноидальная версия разделяет элементы с помощью \0:

readarray -d $'\0' -t IDS < <(jq -r0 '.array[].id' <<< "$MY_JSON")

В зависимости от того, что вы делаете с IDS, вы можете сделать это в JQ а также.

1 голос
/ 12 марта 2020

Оказалось, что это вызвано использованием Python semanti c пробелами. Python ветераны, вероятно, знали это сразу, но я только баловался, так что это сбивало с толку. Я объясню.

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

Python использует отступ вместо видимых символов для определения границ блока кода semanti c (например, тела al oop ). На C -подобном языке (где мне удобнее всего - Java, C# и т. Д. c), мы используем фигурные скобки для этого:

for (var i in myArray) {
    i.doSomething();
    printSomething(i);
}

Нет пробелов важно, поэтому легко превратить его в однострочник (хотя это не распространенная практика в C языках стиля:

for (var i in myArray) { i.doSomething(); printSomething(i); }

Итак, следующее, что я попробовал, это взять больше заботы о пробелах после моих точек с запятой. Моя для l oop была "отступом" на один пробел, тогда как строки import и json.load не имели начальных пробелов. Поэтому я удалил это пробел (я оставляю некоторые окружающего кода для краткости):

python -c "import json,sys;data=json.load(sys.stdin);for a in data['array']: print(a['id'])"

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

  MY_JSON=$(command that returns JSON containing an array of IDs)
  IDS=$(echo "${MY_JSON}" | python -c "import json,sys;data=json.load(sys.stdin);
for a in data['array']:
  print(a['server_id'])")

И оператор for, и тело l oop должны находиться на отдельных отдельных строках, причем тело имеет более глубокий отступ, чем for. Обратите внимание, что этот фрагмент * Скрипт 1039 * попадает в тело if, поэтому все Я с отступом. В своих экспериментах я не смог найти устройство, которое работало бы там, где сам for l oop начинался где угодно, но не в самом начале строки.

Конечный результат не очень красивый, но он работает .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...