Выполнение многострочных операторов в однострочной командной строке?

Я использую Python с -c для выполнения однострочного цикла, то есть:

$ python -c "for r in range(10): print 'rob'"

Это отлично работает. Однако, если я импортирую модуль перед циклом for, я получаю синтаксическую ошибку:

$ python -c "import sys; for r in range(10): print 'rob'"
  File "<string>", line 1
    import sys; for r in range(10): print 'rob'
              ^
SyntaxError: invalid syntax

Есть идеи, как это можно исправить?

Для меня важно, чтобы это было в виде одной строки, чтобы я мог включить его в Makefile.

Ответ 1

ты мог бы сделать

echo -e "import sys\nfor r in range(10): print 'rob'" | python

или без труб:

python -c "exec(\"import sys\nfor r in range(10): print 'rob'\")"

или же

(echo "import sys" ; echo "for r in range(10): print 'rob'") | python

или @SilentGhost answer/@Crast answer

Ответ 2

этот стиль можно использовать и в make файлах (и на самом деле он используется довольно часто).

python - <<EOF
import sys
for r in range(3): print 'rob'
EOF

или

python - <<-EOF
    import sys
    for r in range(3): print 'rob'
EOF

в последнем случае ведущие символы вкладки также удаляются (и может быть достигнут некоторый структурированный прогноз)

вместо EOF может стоять любое маркерное слово, которое не отображается в документе здесь в начале строки (см. также здесь документы в bash manpage или here).

Ответ 3

Проблема на самом деле не связана с операцией import, она с чем-либо находится перед циклом for. Или, более конкретно, все, что появляется перед встроенным блоком.

Например, все они работают:

python -c "import sys; print 'rob'"
python -c "import sys; sys.stdout.write('rob\n')"

Если импорт был выражением, это проблема, но это не работает:

python -c "__import__('sys'); for r in range(10): print 'rob'"

Для вашего основного примера вы можете переписать его следующим образом:

python -c "import sys; map(lambda x: sys.stdout.write('rob%d\n' % x), range(10))"

Однако lambdas может выполнять только выражения, а не инструкции или несколько операторов, поэтому вы все равно не сможете выполнить то, что хотите. Тем не менее, между выражениями генератора, пониманием списка, lambdas, sys.stdout.write, встроенной "map" и некоторой интерполяцией творческой строки вы можете сделать несколько мощных однострочных.

Вопрос в том, как далеко вы хотите пойти, и в какой момент лучше не писать небольшой файл .py, который вместо этого выполняется ваш make файл?

Ответ 4


- Чтобы этот ответ работал с Python 3.x, print вызывается как функция: в 3.x работает только print('foo'), тогда как 2.x также принимает print 'foo'.
- Для кросс-платформенной перспективы, включающей Windows, см. полезный ответ kxr.

В bash, ksh или zsh:

Используйте строку с цитированием ANSI C ($'...'), которая позволяет использовать \n для представления новые строки, которые расширяются до фактических строк новой строки до того, как строка передается на python:

python -c $'import sys\nfor r in range(10): print("rob")'

Обратите внимание на \n между операторами import и for для выполнения разрыва строки.

Чтобы передать значения переменной оболочки для такой команды, безопаснее использовать аргументы и обращаться к ним через sys.argv внутри Python script:

name='rob' # value to pass to the Python script
python -c $'import sys\nfor r in range(10): print(sys.argv[1])' "$name"

См. ниже обсуждение плюсов и минусов использования строки с двойными кавычками (escape-sequence-preprocessed) с встроенными ссылками на переменные оболочки.

Чтобы безопасно работать со строками $'...':

  • Двойные экземпляры \ в исходном исходном коде.
    • \<char> - например, \n в этом случае, но также обычные подозреваемые, такие как \t, \r, \b - расширяются на $'...' (см. man printf для поддерживаемых ускользает)
  • Escape ' экземпляры как \'.

Если вы должны оставаться POSIX-совместимым:

Используйте printf с подстановкой команд:

python -c "$(printf %b 'import sys\nfor r in range(10): print("rob")')"

Чтобы безопасно работать с этим типом строки:

  • Двойные экземпляры \ в исходном исходном коде.
    • \<char> - например, \n в этом случае, но и обычные подозреваемые, такие как \t, \r, \b - расширяются на printf (см. man printf для поддерживаемых escape-последовательности).
  • Передайте строку с одной кавычкой в ​​ printf %b и удалите встроенные одинарные кавычки как '\'' (sic).

    • Использование одинарных кавычек защищает содержимое строки от интерпретации оболочкой.

      • Тем не менее, для коротких сценариев Python (как в этом случае) вы можете использовать строку с двумя кавычками, чтобы включить значения переменных оболочки в свои скрипты - если вы знаете о связанных ошибках (см. следующий точка); например, оболочка расширяет $HOME в текущем домашнем каталоге пользователя. в следующей команде:

        • python -c "$(printf %b "import sys\nfor r in range(10): print('rob is $HOME')")"
      • Однако обычно предпочтительный подход заключается в передаче значений из оболочки через аргументы и доступа к ним через sys.argv в Python; эквивалент указанной выше команды:

        • python -c "$(printf %b 'import sys\nfor r in range(10): print("rob is " + sys.argv[1])')" "$HOME"
    • При использовании строки с двумя кавычками это более удобно - она ​​позволяет использовать встроенные одинарные кавычки без скрытых и встроенных двойных кавычек как \" - она ​​также заставляет строку подвергать интерпретации оболочкой, что может или не может быть намерением; $ и ` в исходном коде, которые не предназначены для оболочки, могут вызывать синтаксическую ошибку или неожиданно изменять строку.

      • Кроме того, обработка оболочки \ в строках с двойными кавычками может мешать; например, чтобы получить Python для выдачи буквенного вывода ro\b, вы должны передать ему ro\\b; с строкой оболочки '...' и удвоенными экземплярами \, мы получаем:
        python -c "$(printf %b 'import sys\nprint("ro\\\\bs")')" # ok: 'ro\bs'
        Напротив, это не работает так, как предполагалось, с строкой оболочки "...":
        python -c "$(printf %b "import sys\nprint('ro\\\\bs')")" # !! INCORRECT: 'rs'
        Оболочка интерпретирует как "\b", так и "\\b" как литерал \b, требуя головокружительного количества дополнительных экземпляров \ для достижения желаемого эффекта:
        python -c "$(printf %b "import sys\nprint('ro\\\\\\\\bs')")"

Чтобы передать код через stdin, а не -c:

Примечание. Я сосредоточен на однолинейных решениях здесь; xorho answer показывает, как использовать многострочный здесь-документ - убедитесь, что цитата разделителя, однако; например, <<'EOF', если вы явно не хотите, чтобы оболочка расширила строку вверх (что связано с указанными выше оговорками).


В bash, ksh или zsh:

Объедините строку с цитированием ANSI C ($'...') с here-string (<<<...):

python - <<<$'import sys\nfor r in range(10): print("rob")'

- сообщает python явно читать из stdin (что он делает по умолчанию). - является необязательным в этом случае, но если вы также хотите передать аргументы сценариям, вам нужно его устранить аргумент из script filename:

python - 'rob' <<<$'import sys\nfor r in range(10): print(sys.argv[1])'

Если вы должны оставаться POSIX-совместимым:

Используйте printf, как указано выше, но с конвейером, чтобы передать свой вывод через stdin:

printf %b 'import sys\nfor r in range(10): print("rob")' | python

С аргументом:

printf %b 'import sys\nfor r in range(10): print(sys.argv[1])' | python - 'rob'

Ответ 5

  Есть идеи, как это можно исправить?

Ваша проблема вызвана тем фактом, что операторы Python, разделенные ;, могут быть только "небольшими операторами", которые являются однострочными. Из файла грамматики в документации по Python:

stmt: simple_stmt | compound_stmt
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
             import_stmt | global_stmt | nonlocal_stmt | assert_stmt)

Составные операторы не могут быть включены в одну строку с другими операторами через точку с запятой - поэтому делать это с флагом -c становится очень неудобно.

При демонстрации Python в среде оболочки bash я считаю очень полезным включать составные операторы. Единственный простой способ сделать это надежно - с помощью heredocs (posix shell).

Heredocs

Используйте heredoc (созданный с помощью <<) и параметр интерфейса командной строки Python command line interface option, -:

$ python - <<-"EOF"
        import sys                    # 1 tab indent
        for r in range(10):           # 1 tab indent
            print('rob')              # 1 tab indent and 4 spaces
EOF

Добавление - после << (<<-) позволяет использовать вкладки для отступа (Stackoverflow преобразует вкладки в пробелы, поэтому я выделил 8 пробелов, чтобы подчеркнуть это). Ведущие вкладки будут удалены.

Вы можете сделать это без вкладок, просто <<:

$ python - << "EOF"
import sys
for r in range(10):
    print('rob')
EOF

Размещение кавычек вокруг EOF предотвращает параметр и арифметическое расширение. Это делает heredoc более устойчивым.

Многострочные строки Bash

Если вы используете двойные кавычки, вы получите расширение оболочки:

$ python -c "
> import sys
> for p in '$PATH'.split(':'):
>     print(p)
> "
/usr/sbin
/usr/bin
/sbin
/bin
...

Чтобы избежать расширения оболочки, используйте одинарные кавычки:

$ python -c '
> import sys
> for p in "$PATH".split(":"):
>     print(p)
> '
$PATH

Обратите внимание, что нам нужно поменять символы кавычек на литералы в Python - мы в основном не можем использовать символы кавычек, интерпретируемые BASH. Мы можем чередовать их, как и в Python, но это уже выглядит довольно странно, поэтому я не рекомендую это:

$ python -c '
import sys
for p in "'"$PATH"'".split(":"):
    print(p)
'
/usr/sbin
/usr/bin
/sbin
/bin
...

Критика принятого ответа (и других)

Это не очень читабельно:

echo -e "import sys\nfor r in range(10): print 'rob'" | python

Не очень читабельно, и дополнительно сложно отладить в случае ошибки:

python -c "exec(\"import sys\\nfor r in range(10): print 'rob'\")"

Возможно, немного более читабельным, но все еще довольно некрасиво:

(echo "import sys" ; echo "for r in range(10): print 'rob'") | python

У вас будет плохое время, если у вас есть " в вашем питоне:

$ python -c "import sys
> for r in range(10): print 'rob'"

Не злоупотребляйте map и не перечисляйте понимания, чтобы получить циклы:

python -c "import sys; map(lambda x: sys.stdout.write('rob%d\n' % x), range(10))"

Это все грустно и плохо. Не делай их.

Ответ 6

просто используйте return и введите его на следующей строке:

[email protected]:~$ python -c "import sys
> for r in range(10): print 'rob'"
rob
rob
...

Ответ 7

Проблема не с оператором import. Проблема в том, что операторы потока управления не работают в команде python. Замените этот оператор import на любой другой оператор, и вы увидите ту же проблему.

Подумайте об этом: python не может встроить все. Он использует отступы для группового управления потоком.

Ответ 8

Если ваша система совместима с Posix.2, она должна предоставить утилиту printf:

$ printf "print 'zap'\nfor r in range(3): print 'rob'" | python
zap
rob
rob
rob

Ответ 9

$ python2.6 -c "import sys; [sys.stdout.write('rob\n') for r in range(10)]"

Прекрасно работает. Используйте "[]", чтобы встроить цикл for.

Ответ 10

(ответил 23 ноября 10 в 19:48) Я не очень большой Pythoner, но однажды я нашел этот синтаксис, забыл, откуда, поэтому я решил записать его:

если вы используете sys.stdout.write вместо print (разница состоит в том, что sys.stdout.write принимает аргументы как функцию, в скобках - в то время как print нет), то для однострочного, вы можете получить с инвертированием порядка команды и for, удаление точки с запятой и включение команды в квадратных скобках, то есть:

python -c "import sys; [sys.stdout.write('rob\n') for r in range(10)]"

Не знаю, как этот синтаксис будет вызываться в Python:)

Надеюсь, что это поможет,

Ура!


(EDIT Tue Apr 9 20:57:30 2013) Ну, я думаю, что, наконец, я нашел, что такое квадратные скобки в одном слое; они "понимают список" (по-видимому); сначала обратите внимание на это в Python 2.7:

$ STR=abc
$ echo $STR | python -c "import sys,re; a=(sys.stdout.write(line) for line in sys.stdin); print a"
<generator object <genexpr> at 0xb771461c>

Таким образом, команда в круглых скобках/скобках рассматривается как "объект-генератор"; если мы "перебираем" через него, вызывая next() - тогда будет выполняться команда внутри скобки (обратите внимание на "abc" на выходе):

$ echo $STR | python -c "import sys,re; a=(sys.stdout.write(line) for line in sys.stdin); a.next() ; print a"
abc
<generator object <genexpr> at 0xb777b734>

Если теперь использовать квадратные скобки - обратите внимание, что нам не нужно вызывать next() для выполнения команды, она выполняется сразу после назначения; однако позже проверка показывает, что a None:

$ echo $STR | python -c "import sys,re; a=[sys.stdout.write(line) for line in sys.stdin]; print a"
abc
[None]

Это не оставляет много информации для случая квадратных скобок - но я наткнулся на эту страницу, которая, я думаю, объясняет:

Советы и хитрости Python - Первое издание - Учебники по Python | Dream.In.Code:

Если вы помните, стандартный формат генератора с одной строкой представляет собой одну линию "для" цикла внутри скобок. Это приведет к повторному объекту "один выстрел", который является объектом, который вы можете перебирать только в одном направлении и который вы не можете повторно использовать, когда достигнете конца.

"Понимание списка" выглядит почти так же, как обычный однострочный генератор, за исключением того, что обычные скобки -() - заменяются квадратными скобками - []. Главным преимуществом алистичного понимания является создание "списка", а не "одноразового" итеративного объекта, так что вы можете идти туда и обратно, добавлять элементы, сортировать и т.д.

И действительно, это список - он только его первый элемент становится без него, как только он выполняется:

$ echo $STR | python -c "import sys,re; print [sys.stdout.write(line) for line in sys.stdin].__class__"
abc
<type 'list'>
$ echo $STR | python -c "import sys,re; print [sys.stdout.write(line) for line in sys.stdin][0]"
abc
None

Пояснения к спискам иначе документируются в 5. Структуры данных: 5.1.4. List Comprehensions - документация Python v2.7.4, поскольку "понимание списков обеспечивает краткий способ создания списков"; предположительно, что там, где ограниченная "исполняемость" списков вступает в игру в однострочных.

Хорошо, надеюсь, что я не слишком уж точно не знаком...

EDIT2: и здесь есть однострочная командная строка с двумя не-вложенными петлями; оба заключены в квадратные скобки "понимание списка":

$ echo $STR | python -c "import sys,re; a=[sys.stdout.write(line) for line in sys.stdin]; b=[sys.stdout.write(str(x)) for x in range(2)] ; print a ; print b"
abc
01[None]
[None, None]

Обратите внимание, что второй "список" b теперь имеет два элемента, так как цикл for for явно явно запускается дважды; однако результат sys.stdout.write() в обоих случаях был (по-видимому) None.

Ответ 11

single/double quotes и backslash везде:

$ python -c 'exec("import sys\nfor i in range(10): print \"bob\"")'

Гораздо лучше:

$ python -c '
> import sys
> for i in range(10):
>   print "bob"
> '

Ответ 12

Этот вариант наиболее переносим для размещения многострочных скриптов в командной строке на Windows и * nix, py2/3, без каналов:

python -c "exec(\"import sys \nfor r in range(10): print('rob') \")"

(Ни один из других примеров, показанных здесь до сих пор, не сделал)

Аккуратно в Windows:

python -c exec"""import sys \nfor r in range(10): print 'rob' """
python -c exec("""import sys \nfor r in range(10): print('rob') """)

Аккуратно на bash/* nix есть:

python -c $'import sys \nfor r in range(10): print("rob")'

Эта функция превращает любой многострочный файл script в переносную команду с одним слоем:

def py2cmdline(script):
    exs = 'exec(%r)' % re.sub('\r\n|\r', '\n', script.rstrip())
    print('python -c "%s"' % exs.replace('"', r'\"'))

Использование:

>>> py2cmdline(getcliptext())
python -c "exec('print \'AA\tA\'\ntry:\n for i in 1, 2, 3:\n  print i / 0\nexcept:\n print \"\"\"longer\nmessage\"\"\"')"

Вход был:

print 'AA   A'
try:
 for i in 1, 2, 3:
  print i / 0
except:
 print """longer
message"""

Ответ 14

Я хотел решение со следующими свойствами:

  1. Удобочитаемый
  2. Прочтите stdin для обработки вывода других инструментов

Оба требования не были предоставлены в других ответах, так что вот как читать stdin при выполнении всего в командной строке:

grep special_string -r | sort | python3 <(cat <<EOF
import sys
for line in sys.stdin:
    tokens = line.split()
    if len(tokens) == 4:
        print("%-45s %7.3f    %s    %s" % (tokens[0], float(tokens[1]), tokens[2], tokens[3]))
EOF
)

Ответ 15

Если вы не хотите касаться stdin и имитировать, как будто вы передали "python cmdfile.py", вы можете сделать следующее из оболочки bash:

$ python  <(printf "word=raw_input('Enter word: ')\nimport sys\nfor i in range(5):\n    print(word)")

Как вы можете видеть, он позволяет использовать stdin для чтения входных данных. Внутренне оболочка создает временный файл для содержимого входной команды.

Ответ 16

Когда мне это нужно, я использую

python -c "$(echo -e "import sys\nsys.stdout.write('Hello World!\\\n')")"

Обратите внимание на тройную обратную косую черту для новой строки в инструкции sys.stdout.write.

Ответ 17

есть еще одна опция, sys.stdout.write возвращает None, которые сохраняют список пустым

cat somefile.log|python -c "import sys;[line for line in sys.stdin if sys.stdout.write(line*2)]"

Ответ 18

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

Конвертер одной строки Python