在单行命令行中执行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中,这一点很重要。
你可以做
echo -e "import sys\nfor r in range(10): print 'rob'" | python
或w / outpipe道:
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可以站在任何标记词没有出现在这里的文件在一行的开始(也可以在这里参阅文档在bash manpage或这里 )。
这个问题实际上不是用import语句,而是for循环之前的任何东西。 或者更具体地说,任何出现在内嵌块之前的东西。
例如,这些全部工作:
python -c "import sys; print 'rob'" python -c "import sys; sys.stdout.write('rob\n')"
如果import作为一个声明是一个问题,这将工作,但它不:
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))"
但是,lambdaexpression式只能执行expression式,而不能执行语句或多个语句,因此您可能仍然无法执行您想要执行的操作。 但是,在生成器expression式,列表理解,lambdaexpression式,sys.stdout.write,内build的“map”和一些创意string插值之间,您可以做一些强大的单行内容。
问题是,你想走多远,写一个你的makefile执行的一个小的.py
文件是不是更好?
– 为了使这个答案也适用于Python 3.x , print
被称为函数 :在3.x中, 只有 print('foo')
有效,而2.x也接受print 'foo'
。
– 对于包含Windows的跨平台视angular,请参阅kxr的有用答案 。
在bash
, ksh
或zsh
:
使用ANSI C引号的string ( $'...'
),该string允许在string传递给python
之前使用\n
来代表扩展为实际换行符的换行符:
python -c $'import sys\nfor r in range(10): print("rob")'
请注意import
和for
语句之间的\n
以产生换行符。
要将shellvariables的值传递给这样的命令,使用参数并通过Python脚本中的sys.argv
访问它们是最安全的:
name='rob' # value to pass to the Python script python -c $'import sys\nfor r in range(10): print(sys.argv[1])' "$name"
请参阅下文,讨论使用带有embedded式 shellvariables引用的(换码序列预处理) 双引号的命令string的优缺点。
安全使用$'...'
string:
- 您的原始源代码中的双
\
实例。- 在这种情况下,例如
\n
这样的序列(比如\n
,还有像\t
,\r
,\b
这样的常见嫌疑犯被$'...'
扩展(参见man printf
获取支持的转义符)
- 在这种情况下,例如
- 转义
'
为'
实例\'
。
如果您必须保持POSIX标准 :
通过命令replace使用printf
:
python -c "$(printf %b 'import sys\nfor r in range(10): print("rob")')"
为了安全地使用这种types的string:
- 您的原始源代码中的双
\
实例。-
\<char>
序列 – 例如在这种情况下\n
,而且像\t
,\r
,\b
– 这样的常见嫌疑犯可以通过printf
进行扩展(请参阅man printf
获取支持的转义序列)。
-
-
将单引号string传递给
printf %b
并将embedded的单引号转义为'\''
(原文如此)。-
使用单引号保护string的内容不被shell解释。
-
也就是说,对于简短的 Python脚本(在这种情况下),您可以使用双引号string将shellvariables值合并到您的脚本中 – 只要您知道相关的陷阱(请参阅下一点)。 例如,shell将
$HOME
扩展到当前用户的home目录。 在下面的命令中:-
python -c "$(printf %b "import sys\nfor r in range(10): print('rob is $HOME')")"
-
-
然而,通常首选的方法是通过参数从shell中传递值,并通过Python中的
sys.argv
来访问它们; 相当于上面的命令是:-
python -c "$(printf %b 'import sys\nfor r in range(10): print("rob is " + sys.argv[1])')" "$HOME"
-
-
-
虽然使用双引号string更方便 – 它允许您使用embedded式单引号未转义和embedded双引号作为
\"
– 它也使得string受shell的解释,可能或可能不是意图;$
和`
源代码中不适用于shell的字符可能会导致语法错误或意外改变string。- 另外,shell中自己
\
处理双引号的string可能会阻碍; 例如,要让Python产生文字输出ro\b
,您必须将ro\\b
传递给它; 用'...'
shellstring和doubled\
实例,我们得到:
python -c "$(printf %b 'import sys\nprint("ro\\\\bs")')" # ok: 'ro\bs'
相反,这不符合预期的"..."
shellstring:
python -c "$(printf %b "import sys\nprint('ro\\\\bs')")" # !! INCORRECT: 'rs'
shell将"\b"
和"\\b"
为文字\b
,需要令人眼花缭乱的额外\
实例来达到所需的效果:
python -c "$(printf %b "import sys\nprint('ro\\\\\\\\bs')")"
- 另外,shell中自己
-
要通过stdin
而不是-c
传递代码 :
注意:我正在关注单线解决scheme。 xorho的答案显示了如何在这里使用多行文件 – 一定要引用分隔符,但是; 例如<<'EOF'
,除非你明确地要求shell在前面展开string(与上面提到的注意事项一起)。
在bash
, ksh
或zsh
:
将ANSI C引用的string ( $'...'
)与一个here-string ( <<<...
)组合在一起:
python - <<<$'import sys\nfor r in range(10): print("rob")'
-
明确告诉python
从标准input读取(默认情况下)。 -
在这种情况下是可选的,但如果您还想将参数传递给脚本,则需要使用它来消除脚本文件名中的参数歧义:
python - 'rob' <<<$'import sys\nfor r in range(10): print(sys.argv[1])'
如果您必须保持POSIX标准 :
像上面那样使用printf
,但是使用一个stream水线来通过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并在下一行input:
user@host:~$ python -c "import sys > for r in range(10): print 'rob'" rob rob ...
$ python2.6 -c "import sys; [sys.stdout.write('rob\n') for r in range(10)]"
工作正常。 使用“[]”来内联for循环。
问题不在于import
语句。 问题是控制stream程语句不能在python命令中内联。 将该import
语句replace为任何其他语句,您将看到相同的问题。
想想看:python不可能内联一切。 它使用缩进来分组控制stream。
如果你的系统符合Posix.2,它应该提供printf
工具:
$ printf "print 'zap'\nfor r in range(3): print 'rob'" | python zap rob rob rob
任何想法如何可以解决?
您的问题是由Python语句分隔的事实创build的;
,只能是“小陈”,都是单行的。 从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
标志来做这件事变得非常不方便。
在bash shell环境中演示Python时,我发现包含复合语句非常有用。 这样做的唯一简单的方法是与heredocs。
here文档
使用heredoc (用<<
创build)和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
在<<
( <<-
)之后添加-
可以使用制表符缩进(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
不太可读,而且在发生错误的情况下难以debugging:
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
或列表parsing来获得for循环:
python -c "import sys; map(lambda x: sys.stdout.write('rob%d\n' % x), range(10))"
这些都是伤心和不好的 不要这样做。
(回答11月23 '10在19:48)我不是一个真正的大Pythoner – 但我发现这个语法一次,忘了从哪里,所以我想我会logging它:
如果你使用sys.stdout.write
而不是print
( 不同之处在于, sys.stdout.write
将参数作为一个函数,在括号中 – 而print
没有 ),那么对于单线程来说,你可以用反转命令的顺序和for
,去掉分号,并将命令括在方括号内,即:
python -c "import sys; [sys.stdout.write('rob\n') for r in range(10)]"
不知道如何在Python中调用这个语法:)
希望这可以帮助,
干杯!
(编辑周二4月9日20:57:30 2013)那么,我想我终于find了这些方括号里面的内容是关于什么的; 他们是“列表理解”(显然); 首先在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 :
如果你还记得,单行生成器的标准格式是括号内的一行“for”循环。 这将产生一个“一次性”的可迭代对象,它是一个只能在一个方向上迭代的对象,一旦到达终点就不能再使用。
“列表理解”看起来几乎与常规的一行生成器相同,只是常规括号 – ()由方括号 – []替代。 alist理解的主要优点是产生一个“列表”,而不是一个“一次性”的可迭代对象,这样你就可以前后通过它,添加元素,sorting等等。
事实上,这是一个列表 – 它只是第一个被执行的元素,
$ 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
列表理解否则logging在5.数据结构:5.1.4。 列表理解 – Python v2.7.4文档作为“列表parsing提供了一个简洁的方式来创build列表”; 据推测,这就是列表中有限的“可执行性”发挥作用的地方。
那么,希望我在这里不是太糟糕了。
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]
注意第二个“list” b
现在有两个元素,因为它的for循环明确地运行了两次; 然而,在这两种情况下sys.stdout.write()
的结果是(显然) None
。
这个脚本提供了一个类似Perl的命令行界面:
Pyliner – 脚本在命令行上运行任意Python代码(Python配方)
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" > '
还有一个选项,sys.stdout.write返回None,它将列表保留为空
cat somefile.log | python -c“import sys; [line for sys.stdin if sys.stdout.write(line * 2)]”
我为此写了一个简单的网页。 您可以在其中粘贴多行代码,并将其转换为可用的单行语句。
Python单行转换器
这个变体是在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\"\"\"')"
input是:
print 'AA A' try: for i in 1, 2, 3: print i / 0 except: print """longer message"""
如果您不想触摸stdin并模拟,就好像您已经传递了“python cmdfile.py”,则可以从bash shell执行以下操作:
$ python <(printf "word=raw_input('Enter word: ')\nimport sys\nfor i in range(5):\n print(word)")
正如你所看到的,它允许你使用stdin来读取input数据。 在内部,shell为input命令内容创build临时文件。
当我需要这样做时,我使用
python -c "$(echo -e "import sys\nsys.stdout.write('Hello World!\\\n')")"
请注意sys.stdout.write语句中换行符的三重反斜杠。