Выполнение многострочных операторов в однострочной командной строке?
Я использую 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")')"
Чтобы безопасно работать с этим типом строки:
Чтобы передать код через 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"""
Ответ 13
Этот script предоставляет интерфейс командной строки, подобный Perl:
Pyliner - script для запуска произвольного кода Python в командной строке (рецепт Python)
Ответ 14
Я хотел решение со следующими свойствами:
- Удобочитаемый
- Прочтите 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