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

Posted

技术标签:

【中文标题】在单行命令行中执行多行语句?【英文标题】:Executing multi-line statements in the one-line command-line? 【发布时间】:2011-01-03 20:20:18 【问题描述】:

我正在使用 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 中。

【问题讨论】:

这能回答你的问题吗? Using a dictionary to select function to execute 【参考方案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's answer/@Crast's answer

【讨论】:

最后一个选项适用于 Windows 中的 python3 @RudolfOlah 希望您现在已经知道了,但仅供参考,您需要为 python3+ 版本包装打印语句,例如:python -c "exec(\"import sys\nfor r in range(10): print('rob')\")" 第二个选项也适用于使用 Python 3 的 Windows。【参考方案2】:

使用带有三引号的 python -c

python -c """
import os
os.system('pwd')
os.system('ls -l')
print('Hello World!')
for _ in range(5):
    print(_)
"""

【讨论】:

【参考方案3】:

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

你的问题是由;分隔的Python语句只允许是“小语句”,它们都是单行的。来自Python docs中的语法文件:

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(一个 posix shell 的东西)。

Heredocs

使用heredoc(使用&lt;&lt; 创建)和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

&lt;&lt;&lt;&lt;-)之后添加- 允许您使用制表符 进行缩进(*** 将制表符转换为空格,因此我缩进了 8 个空格来强调这一点) .前导标签将被剥离。

您可以在没有标签的情况下使用&lt;&lt;

$ python - << "EOF"
import sys
for r in range(10):
    print('rob')
EOF

EOF 周围加上引号可防止parameter 和arithmetic expansion。这使得heredoc更加健壮。

Bash 多行字符串

如果你使用双引号,你会得到 shell-expansion:

$ 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 中有 "'s,你会很糟糕:

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

不要滥用 map 或列表推导来获取 for 循环:

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

这些都是悲伤和糟糕的。不要这样做。

【讨论】:

++ 用于语法信息; (非扩展的)here-doc 很方便,也是最强大的解决方案,但显然不是单行的。如果必须使用单行解决方案,则使用 ANSI C 引用的字符串(bashkshzsh)是一个合理的解决方案:python -c $'import sys\nfor r in range(10): print(\'rob\')'(您只需要担心转义单行引号(可以通过使用双引号来避免)和反斜杠)。 这是一个了不起的答案 - 无论是在 pythonbash 前线【参考方案4】:

我想要一个具有以下属性的解决方案:

    可读 读取标准输入以处理其他工具的输出

其他答案中没有提供这两个要求,所以这里是如何在命令行上执行所有操作时读取标准输入:

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
)

【讨论】:

【参考方案5】:

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

bashkshzsh

使用 ANSI C-quoted string ($'...'),它允许使用 \n 表示在将字符串传递给 python 之前扩展为实际换行符的换行符:

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

注意importfor 语句之间的\n 以实现换行。

将shell变量值传递给这样的命令,使用参数并在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"

关于使用(转义序列预处理)双引号命令字符串和嵌入 shell 变量引用的优缺点的讨论见下文.

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

\ 您的原始源代码中的实例。 \&lt;char&gt; 序列 - 在这种情况下,例如 \n,还有常见的嫌疑人,例如 \t\r\b - 由 $'...' 扩展(有关支持的转义,请参阅 man printf) 将' 实例转义为\'

如果您必须保持 POSIX 兼容

printf 与command substitution 一起使用:

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

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

\ 您的原始源代码中的实例。 \&lt;char&gt; 序列 - 在这种情况下,例如 \n,但还有常见的嫌疑人,例如 \t\r\b - 由 printf 扩展(有关支持的转义序列,请参阅 man printf) .

单引号字符串传递给printf %b,并将嵌入的单引号转义为 '\''(原文如此)。

使用单引号保护字符串的内容不被 shell 解释。

也就是说,对于 short Python 脚本(如本例),您可以使用双引号字符串将 shell 变量值合并到您的脚本中 - 只要你知道相关的陷阱(见下一点);例如,shell 将 $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"

虽然使用 双引号 字符串更方便 - 它允许您使用嵌入式单引号未转义和嵌入式双引号 \" - 它还使由 shell 解释的字符串,这可能是也可能不是意图;源代码中的 $` 字符不适用于 shell 可能会导致语法错误或意外更改字符串。

此外,shell 自己的\ 处理双引号字符串可能会妨碍;例如,要让 Python 产生文字输出 ro\b,您必须将 ro\\b 传递给它;使用'...' shell 字符串和加倍 \ 实例,我们得到: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 将 both "\b""\\b" 解释为文字 \b,需要数量惊人的额外 \ 实例才能达到预期效果:python -c "$(printf %b "import sys\nprint('ro\\\\\\\\bs')")"

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

注意:这里我关注的是单线解决方案; xorho's answer 展示了如何使用多行 here-document - 但是请务必引用分隔符;例如,&lt;&lt;'EOF',除非您明确希望 shell 预先扩展字符串(带有上述警告)。


bashkshzsh

ANSI C-quoted string ($'...') 与 here-string (&lt;&lt;&lt;...) 组合起来:

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

- 明确告诉python 从标准输入读取(默认情况下这样做)。 - 在这种情况下是可选的,但如果您还想将 arguments 传递给脚本,则需要它来消除脚本文件名中的参数歧义:

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

如果您必须保持 POSIX 兼容

如上使用printf,但使用管道以便通过标准输入传递其输出:

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'

【讨论】:

【参考方案6】:

当我需要这样做时,我会使用

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

注意 sys.stdout.write 语句中换行符的三个反斜杠。

【讨论】:

这行得通,但由于您使用的是echo -e,它是非标准的并且需要bashkshzsh,您不妨直接使用$'...' 字符串,这也简化了转义:python -c $'import sys\nsys.stdout.write("Hello World!\\n")' 此答案有效,其他答案不适用于 Python3【参考方案7】:

如果您不想触摸标准输入并像传递“python cmdfile.py”一样进行模拟,您可以从 bash shell 执行以下操作:

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

如您所见,它允许您使用标准输入来读取输入数据。 shell 在内部为输入的命令内容创建临时文件。

【讨论】:

++ 用于不“用完”脚本本身的标准输入(尽管 -c "$(...)" 也这样做,并且符合 POSIX);给&lt;(...) 构造一个名字:process substitution;它也适用于kshzsh【参考方案8】:

此变体最便于在 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")'

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

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"""

【讨论】:

++ 用于跨平台角度和转换器。您的第一个命令在可移植性方面与它一样好(撇开 PowerShell 不谈),但最终没有单一的、完全健壮的跨平台语法,因为需要使用双引号来承担不必要的 shell 扩展的风险,Windows 需要转义与 POSIX-like shell 不同的字符。在 PowerShell v3 或更高版本中,您可以通过在命令字符串参数之前插入“停止解析”选项 --% 来使命令行工作。 @mklement0 "不需要的 shell 扩展":好吧,将 shell 扩展插入到类似 print "path is %%PATH%%" 的东西中。 print "path is $PATH" 通常是人们在脚本或命令行中想要的选项 - 除非人们像往常一样为平台转义。与其他语言相同。 (Python 语法本身并不经常建议以相互竞争的方式使用 % 和 $。) 如果您将 shell 变量引用直接插入到 Python 源代码中,根据定义,它将不可移植。我的观点是,即使您改为在 separate 变量中构建特定于平台的引用,您将 作为参数 传递给 single,“shell-free” Python 命令,可能 并不总是有效,因为您无法保护双引号字符串 portably:例如,如果您需要 literal 怎么办 @ 987654330@ 在你的 Python 代码中?如果您将其转义为 \$foo 以使用类似 POSIX 的 shell,cmd.exe 仍将看到额外的 \ 。这可能很少见,但值得了解。 尝试在 Windows PowerShell 中执行此操作,但问题是 python -c exec("""...""") 不会产生任何输出,无论代码是否在... 是否能够被执行;我可以在那里放任何乱码,结果是一样的。我觉得 shell 正在“吃掉”stdout 和 stderr 流 - 我怎样才能让它吐出来?【参考方案9】:

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

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 手册页中的此处文档或 here)。

【讨论】:

不错。要同时传递参数,只需将它们放在&lt;&lt;EOF 之后。但是请注意,最好引用 分隔符 - 例如,&lt;&lt;'EOF' - 以保护 here-document 的内容免受前期 shell 扩展的影响。 太棒了!这是我想问的更简化的解决方案。【参考方案10】:

还有一个选项,sys.stdout.write 返回 None,使列表保持为空

cat somefile.log|python -c "import sys;[line for line in sys.stdin if sys.stdout.write(line*2)]"

【讨论】:

【参考方案11】:

(2010 年 11 月 23 日 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 中调用此语法:)

希望对你有帮助,

干杯!


(2013 年 4 月 9 日星期二 20:57:30 编辑) 好吧,我想我终于找到了单行中的这些方括号的含义;它们是“列表理解”(显然);首先在 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 Tips And Tricks – First Edition - Python Tutorials | Dream.In.Code:

如果您还记得,单行生成器的标准格式是括号内的一种单行“for”循环。这将产生一个“一次性”可迭代对象,该对象只能在一个方向上进行迭代,并且一旦到达终点就无法重复使用。

“列表推导式”看起来与常规的单行生成器几乎相同,除了常规括号 - ( ) - 被方括号 - [ ] 替换。 alist 理解的主要优点是生成一个“列表”,而不是一个“一次性”可迭代对象,以便您可以在它中来回切换、添加元素、排序等。

确实它是一个列表——它只是它的第一个元素在执行后立即变为无:

$ 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. Data Structures: 5.1.4. List Comprehensions — Python v2.7.4 documentation 中另有说明,因为“列表推导式提供了一种创建列表的简洁方式”;据推测,这就是列表的有限“可执行性”在单行中发挥作用的地方。

好吧,希望我在这里不会太离谱......

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

【讨论】:

【参考方案12】:

这个脚本提供了一个类似 Perl 的命令行界面:

Pyliner - Script to run arbitrary Python code on the command line (Python recipe)

【讨论】:

我认为他们想要解决这个问题,而不是使用其他工具。无论如何很好的提示【参考方案13】:

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

工作正常。 使用 "[ ]" 内联你的 for 循环。

【讨论】:

【参考方案14】:

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"
> '

【讨论】:

【参考方案15】:

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

$ printf "print 'zap'\nfor r in range(3): print 'rob'" | python
zap
rob
rob
rob

【讨论】:

很好的解决方案,但我建议使用printf %b '...' | python 来增加稳健性,因为它可以防止printf 解释输入字符串中的序列,例如%d(格式说明符)。此外,除非您明确希望预先将 shell 扩展应用于您的 Python 命令字符串(这可能会让人感到困惑),否则最好使用 '(单引号)进行外部引用,以避免扩展和反冲处理shell 适用于双引号字符串。【参考方案16】:

问题实际上不在于 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))"

但是,lambdas 只能执行表达式,不能执行语句或多条语句,所以你可能仍然无法做你想做的事情。但是,在生成器表达式、列表推导、lambdas、sys.stdout.write、“map”内置函数和一些创造性的字符串插值之间,你可以做一些强大的单行。

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

【讨论】:

【参考方案17】:

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

想一想:python 不可能内联所有内容。它使用缩进对控制流进行分组。

【讨论】:

【参考方案18】:

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

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

【讨论】:

说真的,如果你继续这样做,你会扭伤一些东西。 python $(srcdir)/myscript.py 为伟大的正义。

以上是关于在单行命令行中执行多行语句?的主要内容,如果未能解决你的问题,请参考以下文章

Linux-MySQL基本命令-SQL语句

1.MySQL基本的命令行操作

如果我在单行数据块笔记本中执行命令会花费更少的时间吗? [关闭]

在命令行中使用 WbExport 时缺少数据和 sql 脚本文件

mysql中如何在命令行中,执行一个SQL脚本文件?

Linux常见命令xargs命令