在单行命令行中执行多行语句?
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(使用<<
创建)和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
在<<
(<<-
)之后添加-
允许您使用制表符 进行缩进(*** 将制表符转换为空格,因此我缩进了 8 个空格来强调这一点) .前导标签将被剥离。
您可以在没有标签的情况下使用<<
:
$ 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 引用的字符串(bash
、ksh
或 zsh
)是一个合理的解决方案:python -c $'import sys\nfor r in range(10): print(\'rob\')'
(您只需要担心转义单行引号(可以通过使用双引号来避免)和反斜杠)。
这是一个了不起的答案 - 无论是在 python
和 bash
前线【参考方案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.x,print
被称为 函数:在 3.x 中,only print('foo')
有效,而 2.x 也接受 print 'foo'
。
- 对于包含 Windows 的跨平台视角,请参阅kxr's helpful answer。
在 bash
、ksh
或 zsh
:
使用 ANSI C-quoted string ($'...'
),它允许使用 \n
表示在将字符串传递给 python
之前扩展为实际换行符的换行符:
python -c $'import sys\nfor r in range(10): print("rob")'
注意import
和for
语句之间的\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 变量引用的优缺点的讨论见下文.
为了安全地使用$'...'
字符串:
\
您的原始源代码中的实例。
\<char>
序列 - 在这种情况下,例如 \n
,还有常见的嫌疑人,例如 \t
、\r
、\b
- 由 $'...'
扩展(有关支持的转义,请参阅 man printf
)
将'
实例转义为\'
。
如果您必须保持 POSIX 兼容:
将printf
与command substitution 一起使用:
python -c "$(printf %b 'import sys\nfor r in range(10): print("rob")')"
要安全地使用这种类型的字符串:
双\
您的原始源代码中的实例。
\<char>
序列 - 在这种情况下,例如 \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 可能会导致语法错误或意外更改字符串。
\
处理双引号字符串可能会妨碍;例如,要让 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 - 但是请务必引用分隔符;例如,<<'EOF'
,除非您明确希望 shell 预先扩展字符串(带有上述警告)。
在 bash
、ksh
或 zsh
:
将 ANSI C-quoted string ($'...'
) 与 here-string (<<<...
) 组合起来:
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
,它是非标准的并且需要bash
、ksh
或zsh
,您不妨直接使用$'...'
字符串,这也简化了转义: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);给<(...)
构造一个名字:process substitution;它也适用于ksh
和zsh
。【参考方案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)。
【讨论】:
不错。要同时传递参数,只需将它们放在<<EOF
之后。但是请注意,最好引用 分隔符 - 例如,<<'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()
来执行命令,它会在分配后立即执行;然而,后来检查发现a
是None
:
$ 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 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"
> '
【讨论】:
【参考方案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
为伟大的正义。以上是关于在单行命令行中执行多行语句?的主要内容,如果未能解决你的问题,请参考以下文章
如果我在单行数据块笔记本中执行命令会花费更少的时间吗? [关闭]