在单行命令行中执行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.xprint被称为函数 :在3.x中, 只有 print('foo')有效,而2.x也接受print 'foo'
– 对于包含Windows的跨平台视angular,请参阅kxr的有用答案 。

bashkshzsh

使用ANSI C引号的string$'...' ),该string允许在string传递给python之前使用\n来代表扩展为实际换行符的换行符:

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

请注意importfor语句之间的\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')")"

通过stdin而不是-c 传递代码

注意:我正在关注单线解决scheme。 xorho的答案显示了如何在这里使用多行文件 – 一定要引用分隔符,但是; 例如<<'EOF' ,除非你明确地要求shell在前面展开string(与上面提到的注意事项一起)。


bashkshzsh

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()来执行命令,它将在赋值时立即执行; 但是,后来的检查显示aNone

 $ 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 quotesbackslash无处不在:

 $ 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语句中换行符的三重反斜杠。