Выполнение многострочных операторов в однострочной командной строке? - PullRequest
168 голосов
/ 11 января 2010

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

4 голосов
/ 23 ноября 2010

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

если вы используете sys.stdout.write вместо print ( разница в том, что sys.stdout.write принимает аргументы как функцию, в скобках - тогда как print не ), то для единицы -Liner, вы можете изменить порядок команды и 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 равно None:

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

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

Python Советы и хитрости - Первое издание - Python Tutorials | Dream.In.Code

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

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

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

$ 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; оба заключены в квадратные скобки «понимание списка»:

$ 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.

3 голосов
/ 26 февраля 2016

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

В башке аккуратно / * nix:

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

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

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"""
2 голосов
/ 16 февраля 2012

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

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

1 голос
/ 27 ноября 2018

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

  1. Читаемые
  2. Чтение стандартного ввода для обработки вывода других инструментов

Оба требования не были представлены в других ответах, поэтому вот как читать 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
)
0 голосов
/ 06 июня 2014

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

cat somefile.log|python -c "import sys;[line for line in sys.stdin if sys.stdout.write(line*2)]"
0 голосов
/ 14 февраля 2013

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

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

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

0 голосов
/ 10 января 2013

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

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

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

...