在单行命令行中执行Python多行语句

178 python shell command-line heredoc

我正在使用Python -c来执行单行循环,即:

$ python -c "for r in range(10): print 'rob'"
Run Code Online (Sandbox Code Playgroud)

这很好用.但是,如果我在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
Run Code Online (Sandbox Code Playgroud)

知道如何解决这个问题吗?

将此作为单行程序对我来说很重要,这样我就可以将它包含在Makefile中.

jsp*_*cal 162

你能做到的

echo -e "import sys\nfor r in range(10): print 'rob'" | python
Run Code Online (Sandbox Code Playgroud)

或没有管道:

python -c "exec(\"import sys\nfor r in range(10): print 'rob'\")"
Run Code Online (Sandbox Code Playgroud)

要么

(echo "import sys" ; echo "for r in range(10): print 'rob'") | python
Run Code Online (Sandbox Code Playgroud)

或@ SilentGhost的答案/@ Crast的答案

  • 这个答案在python3的bash中不起作用 (2认同)
  • @RudolfOlah 希望你现在知道了,但仅供参考,你需要包装 python3+ 版本的打印语句,例如: `python -c "exec(\"import sys\nfor r in range(10): print('rob' )\")"` (2认同)

小智 84

这种风格也可以在makefile中使用(实际上它经常被使用).

python - <<EOF
import sys
for r in range(3): print 'rob'
EOF
Run Code Online (Sandbox Code Playgroud)

要么

python - <<-EOF
    import sys
    for r in range(3): print 'rob'
EOF
Run Code Online (Sandbox Code Playgroud)

在后一种情况下,前导制表符也被删除(并且可以实现一些结构化的前景)

而不是EOF可以代表在行的开头不出现在此处文档中的任何标记词(另请参阅bash联机帮助页或此处的文档).

  • 尼斯.要传递_arguments_,只需将它们放在`<< EOF`之后.但请注意,最好_quote_分隔符 - 例如,`<<'EOF'` - 以便保护[here-document](http://mywiki.wooledge.org/HereDocument)的内容-front _shell_exansions. (9认同)

Cra*_*ast 49

实际上问题不在于import语句,而是在for循环之前的任何事情.或者更具体地说,在内联块之前出现的任何内容.

例如,这些都有效:

python -c "import sys; print 'rob'"
python -c "import sys; sys.stdout.write('rob\n')"
Run Code Online (Sandbox Code Playgroud)

如果导入是一个声明是一个问题,这将有效,但它不会:

python -c "__import__('sys'); for r in range(10): print 'rob'"
Run Code Online (Sandbox Code Playgroud)

对于您的基本示例,您可以将其重写为:

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

但是,lambdas只能执行表达式,而不能执行语句或多个语句,因此您可能仍然无法执行您想要执行的操作.但是,在生成器表达式,列表推导,lambdas,sys.stdout.write,内置的"map"和一些创造性的字符串插值之间,你可以做一些强大的单行.

问题是,你想走多远,在什么时候写一个.py你的makefile执行的小文件不是更好?


mkl*_*nt0 23


- 为了使这个答案也适用于Python 3.x,print被称为函数:在3.x中,只有 print('foo')作用,而2.x也接受print 'foo'.
- 对于包含Windows的跨平台视角,请参阅kxr的有用答案.

bash,kshzsh:

使用ANSI C引用的字符串($'...'),它允许\n在字符串传递到之前用于表示扩展到实际换行符的换行符python:

python -c $'import sys\nfor r in range(10): print("rob")'
Run Code Online (Sandbox Code Playgroud)

注意\nimportfor语句之间实现换行符.

要将shell变量值传递给这样的命令,最安全的方法是使用参数并通过sys.argvPython脚本内部访问它们:

name='rob' # value to pass to the Python script
python -c $'import sys\nfor r in range(10): print(sys.argv[1])' "$name"
Run Code Online (Sandbox Code Playgroud)

请参阅下文,了解使用带有嵌入式 shell变量引用的(转义序列 - 预处理)双引号命令字符串的优缺点.

要安全地使用$'...'字符串:

  • 原始源代码中的\实例.
    • \<char>序列-例如\n在此情况下,也是通常的嫌疑人如\t,\r,\b-由被扩展$'...'(参见man printf用于所支持逃逸)
  • '实例转义为\'.

如果您必须保持POSIX兼容:

使用printf带有命令替换:

python -c "$(printf %b 'import sys\nfor r in range(10): print("rob")')"
Run Code Online (Sandbox Code Playgroud)

要安全地使用这种类型的字符串:

  • 原始源代码中的\实例.
    • \<char>序列-例如\n在此情况下,也是通常的嫌疑人如\t,\r,\b-通过扩展printf(见man printf所支持的转义序列).
  • 单引号字符串传递给printf %b并将嵌入的单引号转义为 '\''(原文如此).

    • 使用单引号可以保护字符串的内容免受shell的解释.

      • 也就是说,对于简短的 Python脚本(如本例所示),您可以使用双引号字符串将shell变量值合并到脚本中 - 只要您知道相关的陷阱(参见下一点); 例如,shell扩展$HOME到当前用户的主目录.在以下命令中:

        • python -c "$(printf %b "import sys\nfor r in range(10): print('rob is $HOME')")"
      • 但是,通常首选的方法是通过参数从shell传递值,并通过sys.argvPython 访问它们; 相当于上面的命令是:

        • python -c "$(printf %b 'import sys\nfor r in range(10): print("rob is " + sys.argv[1])')" "$HOME"
    • 虽然使用双引号字符串更方便 - 它允许您使用未转义和嵌入双引号的嵌入式单引号\"- 它还使字符串受shell的解释,这可能是也可能不是意图; $`在源代码字符不是用于外壳可能会导致语法错误或意外改变的字符串.

      • 另外,shell \在双引号字符串中的处理可能会妨碍它; 例如,要让Python生成文字输出ro\b,你必须传递ro\\b给它; 使用'...'shell字符串和doubled \实例,我们得到:
        python -c "$(printf %b 'import sys\nprint("ro\\\\bs")')" # ok: 'ro\bs'
        相比之下,这不会像shell字符串那样工作"...":
        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:

注意:我在这里专注于单线解决方案; xorho的答案显示了如何在这里使用多行文档 - 请务必引用分隔符; 例如,<<'EOF'除非你明确要求shell预先扩展字符串(这与上面提到的警告一起).


bash,kshzsh:

ANSI C引用的字符串($'...')与here-string(<<<...)组合在一起:

python - <<<$'import sys\nfor r in range(10): print("rob")'
Run Code Online (Sandbox Code Playgroud)

-python明确告诉从stdin读取(默认情况下它). -在这种情况下是可选的,但是如果你还想将参数传递给脚本,你需要它来消除脚本文件名中的参数:

python - 'rob' <<<$'import sys\nfor r in range(10): print(sys.argv[1])'
Run Code Online (Sandbox Code Playgroud)

如果您必须保持POSIX兼容:

使用printf如上所述,但使用管道以便通过stdin传递其输出:

printf %b 'import sys\nfor r in range(10): print("rob")' | python
Run Code Online (Sandbox Code Playgroud)

有一个论点:

printf %b 'import sys\nfor r in range(10): print(sys.argv[1])' | python - 'rob'
Run Code Online (Sandbox Code Playgroud)


Aar*_*all 16

知道如何解决这个问题吗?

你的问题是由以下事实造成的: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)
Run Code Online (Sandbox Code Playgroud)

复合语句不能通过分号与其他语句包含在同一行 - 因此使用该-c标志执行此操作会变得非常不方便.

在bash shell环境中演示Python时,我发现包含复合语句非常有用.这样做的唯一简单方法是使用heredocs.

here文档

使用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
Run Code Online (Sandbox Code Playgroud)

添加-after <<(the <<-)允许你使用制表符缩进(Stackoverflow将制表符转换为空格,所以我缩进了8个空格来强调这一点).前导标签将被剥离.

您可以在没有选项卡的情况下执行此操作<<:

$ python - << "EOF"
import sys
for r in range(10):
    print('rob')
EOF
Run Code Online (Sandbox Code Playgroud)

放置引号EOF可防止参数算术扩展.这使得heredoc更加强大.

批准接受的答案(和其他人)

这不是很可读:

$ python -c "
> import sys
> for p in '$PATH'.split(':'):
>     print(p)
> "
/usr/sbin
/usr/bin
/sbin
/bin
...
Run Code Online (Sandbox Code Playgroud)

不太可读,并且在出现错误的情况下另外难以调试:

$ python -c '
> import sys
> for p in "$PATH".split(":"):
>     print(p)
> '
$PATH
Run Code Online (Sandbox Code Playgroud)

也许更具可读性,但仍然相当难看:

$ python -c '
import sys
for p in "'"$PATH"'".split(":"):
    print(p)
'
/usr/sbin
/usr/bin
/sbin
/bin
...
Run Code Online (Sandbox Code Playgroud)

如果你有"你的python,你将度过一段美好的时光:

echo -e "import sys\nfor r in range(10): print 'rob'" | python
Run Code Online (Sandbox Code Playgroud)

不要滥用map或列出理解来获得for循环:

python -c "exec(\"import sys\\nfor r in range(10): print 'rob'\")"
Run Code Online (Sandbox Code Playgroud)

这些都是悲伤和坏事.不要这样做.

  • ++用于语法信息; (非扩展)here-doc是方便且最强大的解决方案,但显然不是单行.如果必须使用单行解决方案,那么使用ANSI C引用的字符串(`bash`,`ksh`或`zsh`)是一个合理的解决方案:`python -c $'导入sys \n for r in range( 10):print(\'rob \')'`(你只需要担心转义单引号(你可以通过使用双引号来避免)和反斜杠). (2认同)

Sil*_*ost 12

只需使用return并在下一行输入:

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

  • 说真的,如果你继续这样做,你会扭伤一些东西.`python $(srcdir)/ myscript.py`非常正义. (6认同)

kxr*_*kxr 10

此变体最适合在 Windows 和类 Unix 系统(Python\xc2\xa02 和 Python\xc2\xa03)的命令行上放置多行脚本,无需使用管道:

\n
python -c "exec(\\"import sys \\nfor r in range(10): print('rob') \\")"\n
Run Code Online (Sandbox Code Playgroud)\n

(到目前为止,这里看到的其他示例都没有这样做。)

\n

Windows 上的整洁是:

\n
python -c exec"""import sys \\nfor r in range(10): print 'rob' """\npython -c exec("""import sys \\nfor r in range(10): print('rob') """)\n
Run Code Online (Sandbox Code Playgroud)\n

在类 Unix 系统上的 Bash 上,Neat 是:

\n
python -c $'import sys \\nfor r in range(10): print("rob")'\n
Run Code Online (Sandbox Code Playgroud)\n
\n

此函数将任何多行脚本转换为可移植的单行命令:

\n
def py2cmdline(script):\n    exs = 'exec(%r)' % re.sub('\\r\\n|\\r', '\\n', script.rstrip())\n    print('python -c "%s"' % exs.replace('"', r'\\"'))\n
Run Code Online (Sandbox Code Playgroud)\n

用法:

\n
>>> py2cmdline(getcliptext())\npython -c "exec('print \\'AA\\tA\\'\\ntry:\\n for i in 1, 2, 3:\\n  print i / 0\\nexcept:\\n print \\"\\"\\"longer\\nmessage\\"\\"\\"')"\n
Run Code Online (Sandbox Code Playgroud)\n

输入是:

\n
print 'AA    A'\ntry:\n for i in 1, 2, 3:\n  print i / 0\nexcept:\n print """longer\nmessage"""\n
Run Code Online (Sandbox Code Playgroud)\n

  • 尝试在 Windows PowerShell 中执行此操作,但问题是 python -c exec("""...""") 不会产生任何输出,无论 ... 中的代码是否能够执行或不; 我可以在那里放任何乱码,结果都是一样的。我觉得 shell 正在“吃掉”stdout 和 stderr 流 - 我怎样才能让它把它们吐出来? (2认同)

Dav*_*ger 9

问题不在于import声明.问题是控制流语句不能在python命令中内联工作.将该import语句替换为任何其他语句,您将看到相同的问题.

想想看:python不可能内联一切.它使用缩进来分组控制流.


Ale*_*lli 7

如果您的系统符合Posix.2,则应提供该printf实用程序:

$ printf "print 'zap'\nfor r in range(3): print 'rob'" | python
zap
rob
rob
rob
Run Code Online (Sandbox Code Playgroud)

  • 很好的解决方案,但我建议使用`printf%b'...'| python`增加了健壮性,因为它阻止了`printf`来解释输入字符串中的`%d`(格式说明符)等序列.另外,除非你明确想要预先将Python扩展应用于Python命令字符串(这可能会让人感到困惑),否则最好使用```(单引号)进行外引用,以避免扩展和反向处理shell适用于双引号字符串. (2认同)

小智 7

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

工作良好.使用"[]"来内联你的for循环.