我正在使用带-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中。
答案 0 :(得分:151)
你可以做到
echo -e "import sys\nfor r in range(10): print 'rob'" | python
或w / out pipe:
python -c "exec(\"import sys\nfor r in range(10): print 'rob'\")"
或
(echo "import sys" ; echo "for r in range(10): print 'rob'") | python
答案 1 :(得分:80)
这种风格也可以在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)。
答案 2 :(得分:44)
实际上问题不在于import语句,而是在for循环之前的任何事情。或者更具体地说,在内联块之前出现的任何内容。
例如,这些都有效:
python -c "import sys; print 'rob'"
python -c "import sys; sys.stdout.write('rob\n')"
如果导入是一个声明是一个问题,这可行,但它不会:
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”和一些创造性的字符串插值之间,你可以做一些强大的单行。
问题是,你想走多远,在什么时候写一个你的makefile执行的小.py
文件不是更好?
答案 3 :(得分:20)
- 为了使这个答案也适用于Python 3.x ,print
被称为函数:在3.x中,仅 print('foo')
有效,而2.x也接受print 'foo'
- 对于包含 Windows 的跨平台视角,请参阅kxr's helpful answer。
在 bash
,ksh
或zsh
中:
使用 ANSI C-quoted string ($'...'
),允许使用\n
来表示在字符串传递给{{{}之前扩展到实际换行符的换行符1}}:
python
请注意python -c $'import sys\nfor r in range(10): print("rob")'
和\n
语句之间的import
以实现换行符。
要将shell变量值传递给这样的命令,最安全的方法是使用 arguments 并通过Python脚本中的for
访问它们:
sys.argv
请参阅下文,了解使用带有嵌入式 shell变量引用的(转义序列 - 预处理)双引号命令字符串的优缺点
使用name='rob' # value to pass to the Python script
python -c $'import sys\nfor r in range(10): print(sys.argv[1])' "$name"
字符串安全地工作:
$'...'
个实例。
\
序列(例如\<char>
在这种情况下,而且通常的嫌疑人,例如\n
,\t
,\r
- 被{扩展{ {1}}(有关受支持的转义信息,请参阅\b
)$'...'
个实例转发为man printf
。如果您必须保持 POSIX兼容:
一起使用\'
使用此类字符串安全地工作:
printf
个实例。
python -c "$(printf %b 'import sys\nfor r in range(10): print("rob")')"
序列(例如\
在这种情况下,而且通常的嫌疑人,例如\<char>
,\n
,\t
- 被{扩展{ {1}}(有关支持的转义序列,请参阅\r
。)将单引号字符串传递给\b
,将嵌入的单引号转义为 printf
(原文如此)。
使用单引号可以保护字符串的内容免受 shell 的解释。
也就是说,对于短 Python脚本(如本例所示),您可以使用双引号字符串将 shell 变量值合并到脚本中 - 只要你知道相关的陷阱(见下一点);例如,shell将man printf
扩展为当前用户的主目录。在以下命令中:
printf %b
但是,通常首选的方法是通过 arguments 从shell传递值,并通过Python中的'\''
访问它们;相当于上面的命令是:
$HOME
使用双引号字符串更方便 - 它允许您使用嵌入式单引号未转义和嵌入双引号python -c "$(printf %b "import sys\nfor r in range(10): print('rob is $HOME')")"
- 它还使字符串受 shell 的解释,这可能是也可能不是意图;源代码中的sys.argv
和python -c "$(printf %b 'import sys\nfor r in range(10): print("rob is " + sys.argv[1])')" "$HOME"
字符不适用于shell,可能会导致语法错误或意外更改字符串。
\"
处理可能会妨碍;例如,要使 Python 生成文字输出$
,您必须将`
传递给它;使用\
shell字符串和 doubled ro\b
个实例,我们得到:ro\\b
'...'
shell字符串工作:\
python -c "$(printf %b 'import sys\nprint("ro\\\\bs")')" # ok: 'ro\bs'
和"..."
解释为文字python -c "$(printf %b "import sys\nprint('ro\\\\bs')")" # !! INCORRECT: 'rs'
,需要一些令人眼花缭乱的额外"\b"
个实例才能达到预期效果:"\\b"
通过\b
而不是\
传递代码:
注意:我在这里专注于单个 -line解决方案; xorho's answer显示了如何使用多行here-document - 请确保引用分隔符;例如,python -c "$(printf %b "import sys\nprint('ro\\\\\\\\bs')")"
,除非你明确要求shell预先扩展字符串(这与上面提到的警告一起提供)。
在 stdin
,-c
或<<'EOF'
中:
将 ANSI C-quoted string (bash
)与here-string(ksh
)相结合:
zsh
$'...'
显式地告诉<<<...
从stdin读取(默认情况下它是这样做的)。
在这种情况下,python - <<<$'import sys\nfor r in range(10): print("rob")'
是可选的,但如果您还想将参数传递给脚本,则需要它从脚本文件名中消除歧义:
-
如果您必须保持 POSIX兼容:
如上所述使用python
,但使用管道以便通过stdin传递其输出:
-
有一个论点:
python - 'rob' <<<$'import sys\nfor r in range(10): print(sys.argv[1])'
答案 4 :(得分:12)
知道如何解决这个问题吗?
你的问题是由于;
分隔的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。
使用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
在-
之后添加<<
(<<-
)允许您使用标签进行缩进(Stackoverflow将标签转换为空格,因此我缩进了8个空间来强调这一点)。前导标签将被剥离。
您可以在没有仅<<
$ python - << "EOF"
import sys
for r in range(10):
print('rob')
EOF
在EOF
周围加上引号会阻止parameter和arithmetic expansion。这使得heredoc更加健壮。
这不是很易读:
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-loops:
python -c "import sys; map(lambda x: sys.stdout.write('rob%d\n' % x), range(10))"
这些都是悲伤和坏事。不要这样做。
答案 5 :(得分:11)
只需使用return并在下一行输入:
user@host:~$ python -c "import sys
> for r in range(10): print 'rob'"
rob
rob
...
答案 6 :(得分:8)
问题不在于import
语句。问题是控制流语句不能在python命令中内联工作。将import
语句替换为任何其他语句,您将看到相同的问题。
想一想:python不可能内联一切。它使用缩进来对控制流进行分组。
答案 7 :(得分:7)
如果您的系统符合Posix.2,则应提供printf
实用程序:
$ printf "print 'zap'\nfor r in range(3): print 'rob'" | python
zap
rob
rob
rob
答案 8 :(得分:7)
$ python2.6 -c "import sys; [sys.stdout.write('rob\n') for r in range(10)]"
工作正常。 使用“[]”来内联你的循环。
答案 9 :(得分:4)
(2010年11月23日19:48回答) 我不是一个真正的大蟒蛇 - 但我发现这个语法一次,忘了从哪里来,所以我想我会记录它:
如果您使用sys.stdout.write
而不是print
(区别在于,sys.stdout.write
将参数作为函数加在括号中 - 而print
不会< / em>),然后对于单行,您可以通过反转命令和for
的顺序,删除分号,并将命令括在方括号中,即:
python -c "import sys; [sys.stdout.write('rob\n') for r in range(10)]"
不知道如何在Python中调用这种语法:)
希望这有帮助,
干杯!
(EDIT Tue Apr 9 20:57:30 2013)好吧,我想我终于找到了这些方括号中的方括号;他们是“列表理解”(显然);首先在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”循环。这将生成一个“一次性”可迭代对象,这个对象只能在一个方向上迭代,一旦到达终点就无法重复使用。
'list comprehension'看起来与普通的单行生成器几乎相同,只是常规括号 - ( - )被方括号 - []替换。 alist理解的主要优点是产生一个'list'而不是'one-shot'可迭代对象,这样你就可以在它中来回移动,添加元素,排序等等。
实际上它是一个列表 - 它只是第一个元素在执行后变为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中另有记录,“列表理解提供了创建列表的简明方法”;据推测,这就是列表中有限的“可执行性”在单行中发挥作用的地方。
嗯,希望我在这里并不是太过分了......
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
。
答案 10 :(得分:3)
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"
> '
答案 11 :(得分:3)
这种变体最便于将多行脚本放在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"""
答案 12 :(得分:2)
此脚本提供类似Perl的命令行界面:
Pyliner - Script to run arbitrary Python code on the command line (Python recipe)
答案 13 :(得分:1)
我想要一个具有以下属性的解决方案:
在其他答案中均未提供这两个要求,因此,这是在命令行上执行所有操作时如何读取stdin的方法:
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
)
答案 14 :(得分:1)
使用带有三重引号的 python -c
python -c """
import os
os.system('pwd')
os.system('ls -l')
print('Hello World!')
for _ in range(5):
print(_)
"""
答案 15 :(得分:0)
还有一个选项,sys.stdout.write返回None,这使列表保持空白
cat somefile.log|python -c "import sys;[line for line in sys.stdin if sys.stdout.write(line*2)]"
答案 16 :(得分:0)
当我需要这样做时,我使用
python -c "$(echo -e "import sys\nsys.stdout.write('Hello World!\\\n')")"
请注意sys.stdout.write语句中换行符的三重反斜杠。
答案 17 :(得分:0)
如果您不想触摸标准输入并模拟已经通过“python cmdfile.py”,则可以从bash shell执行以下操作:
$ python <(printf "word=raw_input('Enter word: ')\nimport sys\nfor i in range(5):\n print(word)")
如您所见,它允许您使用stdin读取输入数据。 shell内部为输入命令内容创建临时文件。