我正在使用带有 -c
的 Python 来执行单行循环,即:
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
或者没有管道:
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。
这种风格也可以在 makefile 中使用(实际上它经常使用)。
python - <<EOF
import random
for r in range(3): print(random.randint(1, 42))
EOF
或使用硬标签:
python - <<-EOF
import random
for r in range(3): print(random.randint(1, 42))
EOF
# Important: Replace the indentation above w/ hard tabs.
在上述情况下,前导 TAB 字符也被删除(并且可以实现一些结构化的外观)。
代替 EOF 可以将任何未出现在此处文档中的标记词置于行首(另请参见 bash 手册页或 here 中的此处文档)。
问题实际上不在于 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 执行不是更好?
- 为了使这个答案也适用于 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 变量引用的优缺点的讨论,请参见下文。
要安全地使用 $'...'
字符串:
原始源代码中的双 \ 实例。 \
\
将 ' 实例转义为 \'。
如果您必须保持 POSIX 兼容:
将 printf
与 command substitution 一起使用:
python -c "$(printf %b 'import sys\nfor r in range(10): print("rob")')"
要安全地使用这种类型的字符串:
原始源代码中的双 \ 实例。 \
\
将单引号字符串传递给 printf %b 并将嵌入的单引号转义为 '\'' (原文如此)。使用单引号可以保护字符串的内容不被 shell 解释。也就是说,对于简短的 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 解释两个 "\b"和 "\\b" 作为文字 \b,需要令人眼花缭乱的额外 \ 实例才能达到预期的效果: python -c "$(printf %b "import sys\nprint('ro\\\\\\\\ bs')")"
使用单引号可以保护字符串的内容不被 shell 解释。也就是说,对于简短的 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"
也就是说,对于简短的 Python 脚本(如本例),您可以使用双引号字符串将 shell 变量值合并到您的脚本中——只要您知道相关的陷阱(请参阅下一点);例如,shell 将 $HOME 扩展为当前用户的主目录。在以下命令中: python -c "$(printf %b "import sys\nfor r in range(10): print('rob is $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"
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 将 "\b" 和 "\\b" 都解释为文字 \b,需要数量惊人的额外 \ 实例达到预期效果: python -c "$(printf %b "impo rt sys\nprint('ro\\\\\\\\bs')")"
此外,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')")" # ! !错误:'rs' shell 将 "\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'
知道如何解决这个问题吗?
您的问题是由 ;
分隔的 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
在 <<
(<<-
)之后添加 -
允许您使用 tabs 进行缩进(Stackoverflow 将制表符转换为空格,因此我缩进了 8 个空格来强调这一点)。前导标签将被剥离。
您可以在没有标签的情况下仅使用 <<
:
$ python - << "EOF"
import sys
for r in range(10):
print('rob')
EOF
在 EOF
周围加上引号可防止 parameter 和 arithmetic expansion。这使得heredoc更加健壮。
Bash 多行字符串
如果你使用双引号,你会得到 shell 扩展:
$ 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 中有 "
,您将度过一段糟糕的时光:
$ 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))"
这些都是可悲和不好的。不要做他们。
bash
、ksh
或 zsh
)是一个合理的解决方案:python -c $'import sys\nfor r in range(10): print(\'rob\')'
(您只需要担心转义单行引号(可以通过使用双引号来避免)和反斜杠)。
python
和 bash
方面
只需使用 Return 并在下一行输入:
python -c "import sys
for r in range(10): print 'rob'"
rob
rob
...
python $(srcdir)/myscript.py
为大正义。
python2.6 -c "import sys; [sys.stdout.write('rob\n') for r in range(10)]"
工作正常。
使用 "[ ]" 内联你的 for 循环。
问题不在于 import
语句。问题是控制流语句不能在 Python 解释器命令中内联工作。将该 import
语句替换为任何其他语句,您将看到同样的问题。
想一想:Python 不可能内联所有内容。它使用缩进对控制流进行分组。
如果您的系统符合 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 适用于双引号字符串。
此变体最便于在 Windows 和类 Unix 系统(Python 2 和 Python 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') """)
Neat on Bash 在类 Unix 系统上是:
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"""
--%
来使命令行工作。
print "path is %%PATH%%"
等内容中。 print "path is $PATH"
通常是脚本或命令行中需要的选项 - 除非您像往常一样为平台转义。与其他语言相同。 (Python 语法本身并不经常建议以相互竞争的方式使用 % 和 $。)
\$foo
以便使用类似 POSIX 的 shell,则 cmd.exe
仍会看到额外的 \
。这可能很少见,但值得了解。
我并不是真正的 Python 爱好者——但我曾经发现过这种语法,忘记了从哪里来,所以我想我会记录下来:
如果您使用 sys.stdout.write
而不是 print
(区别在于,sys.stdout.write
将参数作为函数,在括号中 - 而 print
没有),那么对于单行,您可以通过反转命令和 for
的顺序,删除分号并将命令括在方括号中,即:
python -c "import sys; [sys.stdout.write('rob\n') for r in range(10)]"
我不知道如何在 Python 中调用这种语法 :)
单行中的这些方括号是“列表推导”;在 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 理解的主要优点是产生一个“列表”,而不是一个“一次性”可迭代对象,这样您就可以在它中来回切换、添加元素、排序等。
实际上它是一个列表——它只是它的第一个元素在执行后立即变为 none:
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 中以其他方式记录为“列表推导提供了一种创建列表的简明方式”;据推测,这就是列表的有限“可执行性”在单行中发挥作用的地方。
好吧,希望我在这里不会太离谱......
这是一个带有两个非嵌套 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
。
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")
'
注意:在某些系统上,可执行文件 python
用于 Python 2,可能不可用。可执行文件 python3
用于 Python 3。
该脚本提供了一个类似 Perl 的命令行界面:
Pyliner - Script to run arbitrary Python code on the command line (Python recipe)
我想要一个具有以下属性的解决方案:
可读 读取标准输入以处理其他工具的输出
其他答案中没有提供这两个要求,所以这里是如何在命令行上执行所有操作时读取标准输入:
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
)
当我需要这样做时,我使用了:
python -c "$(echo -e "import sys\nsys.stdout.write('Hello, World!\\\n')")"
请注意 sys.stdout.write 语句中换行符的三重反斜杠。
echo -e
,它需要 bash
、ksh
或 zsh
,您不妨直接使用 $'...'
字符串,这也简化了转义:{ 6}
如果您不想触摸 standard input 并像传递“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
。
将 python -c
与三引号一起使用:
python -c """
import os
os.system('pwd')
os.system('ls -l')
print('Hello, World!')
for _ in range(5):
print(_)
"""
还有一种选择。 sys.stdout.write 返回 None,使列表保持为空:
cat somefile.log|python -c "import sys;[line for line in sys.stdin if sys.stdout.write(line*2)]"
<pre style='color:#000000;background:#ffffff;'><html><body style='color:#000000; background:#ffffff; '><pre>
cat somefile<span style='color:#800000; font-weight:bold; '>.</span>log<span style='color:#e34adc; '>|<span style='color:#7f0055; font-weight:bold; '>python -c "import</span> sys;[line <span style='color:#7f0055; font-weight:bold; '>for</span> line <span style='color:#7f0055; font-weight:bold; '>in</span> sys.stdin <span style='color:#7f0055; font-weight:bold; '>if</span> sys.stdout.write(line*2)]" </pre>
(!!!!)
<pre>
标记不平衡(两个开始标记和一个结束标记)。 pre
不需要结束标记(以某种形式)吗? (即使那样它也可能不起作用。)
python -c "exec(\"import sys\nfor r in range(10): print('rob')\")"