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

Я использую 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.

17 ответов


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

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 / @Crast это


этот стиль также можно использовать в makefiles (и на самом деле он используется довольно часто).

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 может стоять любое маркерное слово, не появляющееся в документе here в начале строки (см. Также здесь документы на странице bash или здесь).


проблема на самом деле не с оператором 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.стандартный вывод.напишите," карта " встроенный, и некоторые творческие строки интерполяции, вы можете сделать некоторые мощные однострочные.

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



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

на bash, ksh или zsh:

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

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

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

до pass shell-значения переменных для такой команды безопаснее всего использовать аргументы и sys.argv внутри скрипта Python :

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)двойные кавычки командную строку с встроенный Shell-переменной-ссылки.

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

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

если вы должны оставаться 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).

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

      • что сказано, ибо короче скрипты Python (как в этом случае) вы можете использовать строку с двойными кавычками для включения shell значения переменных в ваших скриптах-пока вы знаете о связанных подводных камнях (см. Следующий пункт); например, оболочка расширяется $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"
    • при использовании двойные кавычки строк больше удобно - он позволяет использовать встроенные одинарные кавычки без эскапады и встроенные двойные кавычки как \" - это строка подлежит толкованию shell, что может быть или не быть намерением;$ и ` символы в исходном коде, которые не предназначены для оболочки может вызвать синтаксическую ошибку или неожиданно изменить строку.

      • кроме того, оболочки собственный \ обработка в строках с двойными кавычками может помешать; например, получить питон произвести прямом выходе 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 ответ показывает, как использовать многострочный здесь-документ - обязательно цитата разделитель, однако; например, <<'EOF', если вы явно не хотите, чтобы оболочка расширяла строку спереди (которая поставляется с оговорками, указанными выше).


на bash, ksh или zsh:

сочетают в себе ANSI C-цитируемая строка ($'...') С строка (<<<...):

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

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

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'

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

user@host:~$ python -c "import sys
> for r in range(10): print 'rob'"
rob
rob
...

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

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

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.

Heredocs

использовать помощи heredoc (создано с помощью <<) и в Python опция интерфейса командной строки, -:

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

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

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

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

поставив кавычки EOF предупреждает параметр и арифметические расширения. Это делает heredoc более надежным.

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

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

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:

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

не злоупотребляйте map или Перечислите понимания, чтобы получить for-loops:

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

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


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

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


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

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

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

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


(ответили 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 :)

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

Ура!


(правка Вт апрель 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 is None:

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

это не оставляет много информации для поиска, для площади скобки case-но я наткнулся на эту страницу, которая, я думаю, объясняет:

Python Советы И Рекомендации – Первое Издание - Python Учебники | Мечта.Внутри.Код:

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

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

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

$ 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. Список постижений-Python v2.7.4 документация как "понимание списка обеспечивает краткий способ создания списков"; предположительно, именно здесь ограниченная" исполняемость " списков вступает в игру в однострочных.

ну, надеюсь, я не слишком ошибаюсь здесь ...

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

$ 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 явно выполняется дважды; однако результат sys.stdout.write() в обоих случаях был (видимо) None.


этот вариант наиболее переносим для размещения многострочных скриптов в командной строке на 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') """)

аккуратный на баш/ * Никс:

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

эта функция превращает любой многострочный скрипт в портативный command-one-liner:

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"""

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"
> '

этот скрипт предоставляет Perl-подобный интерфейс командной строки:

Pyliner-скрипт для запуска произвольного кода Python в командной строке (рецепт Python)


есть еще один вариант, sys.стандартный вывод.write возвращает None, которые держат список пустым

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

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

Python Однострочный Конвертер


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

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

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


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

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

Примечание тройной слеш в строку в sys.стандартный вывод.письменное заявление.