在单行命令行中执行多行语句
- 2025-02-10 08:57:00
- admin 原创
- 49
问题描述:
我正在使用 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中。
解决方案 1:
你可以做
echo -e "import sys
for r in range(10): print 'rob'" | python
或者不使用管道:
python -c "exec(\"import sys
for r in range(10): print 'rob'\")"
或者
(echo "import sys" ; echo "for r in range(10): print 'rob'") | python
或者SilentGhost 的回答或者Crast 的回答。
解决方案 2:
这种风格也可以在 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 手册页中的此处文档或此处)。
解决方案 3:
问题实际上不在于import语句。问题在于for循环之前的任何内容。或者更具体地说,是内联块之前出现的任何内容。
例如,这些都有效:
python -c "import sys; print 'rob'"
python -c "import sys; sys.stdout.write('rob
')"
如果 import 是一个声明的问题,那么这将起作用,但事实并非如此:
python -c "__import__('sys'); for r in range(10): print 'rob'"
对于非常基本的示例,您可以将其重写如下:
python -c "import sys; map(lambda x: sys.stdout.write('rob%d
' % x), range(10))"
但是,lambda 只能执行表达式,而不能执行语句或多个语句,因此您可能仍然无法完成想要做的事情。但是,在生成器表达式、列表推导、lambda、sys.stdout.write、“map”内置函数和一些创造性的字符串插值之间,您可以执行一些强大的单行代码。
问题是,您想要走多远,以及在什么时候编写一个小.py
文件并让您的 makefile 来执行不是更好吗?
解决方案 4:
为了使这个答案也
适用于 Python 3.xprint
,将其作为函数调用:仅 在 3.x 中print('foo')
有效,而 2.x 也接受print 'foo'
。 - 有关包括Windows
在内的跨平台视角,请参阅kxr 的有用答案。
在bash
、ksh
或 中zsh
:
使用ANSI C 引用字符串( $'...'
),它允许使用`来表示在将字符串传递给 之前扩展为实际换行符的换行符
python`:
python -c $'import sys
for r in range(10): print("rob")'
注意和语句`之间的换行效果。
import`for
要将shell 变量值传递给这样的命令,最安全的方式是使用参数sys.argv
并通过Python 脚本内部 访问它们:
name='rob' # value to pass to the Python script
python -c $'import sys
for r in range(10): print(sys.argv[1])' "$name"
请参阅下文有关使用带有嵌入shell 变量引用的(转义序列预处理的)双引号命令字符串的优缺点的讨论。
要安全地使用$'...'
字符串:
原始源代码中的双重``实例。
+ `<char>`序列(例如`
本例中的情况,还有常见的情况,例如
、
、
)被扩展为
$'...'(请参阅 了解
man printf`支持的转义符)
将
'
实例转义为'
。
如果必须保持符合 POSIX 标准:
printf
与命令替换一起使用:
python -c "$(printf %b 'import sys
for r in range(10): print("rob")')"
要安全地使用这种类型的字符串:
原始源代码中的双重``实例。
+ `<char>`序列(例如`
本例中所示的序列,以及常见的序列,例如
、
、
)被扩展
printf(请参阅
man printf`以了解支持的转义序列)。
将单引号字符串传递给
printf %b
并将嵌入的单引号转义为'''
(sic)。
+ 使用单引号可以保护字符串的内容不被 shell*解释*。
- 也就是说,对于*简短的*Python 脚本(如本例所示),您可以使用双引号字符串将*shell*变量值合并到您的脚本中 - 只要您了解相关的陷阱(参见下一点);例如,shell 扩展`$HOME`为当前用户的主目录。在以下命令中:
* `python -c "$(printf %b "import sys
for r in range(10): print('rob is $HOME')")"`
- 然而,通常首选的方法是通过*参数*从 shell 传递值,并通过`sys.argv`Python 访问它们;上述命令的等效方法是:
* `python -c "$(printf %b 'import sys
for r in range(10): print("rob is " + sys.argv[1])')" "$HOME"`
+ 虽然使用*双引号*字符串更*方便*- 它允许您使用未转义的嵌入单引号和嵌入双引号`\"`- 但它也使得字符串受制于 shell 的解释*,*这可能是也可能不是意图;`$`并且```源代码中不适合 shell 的字符可能会导致语法错误或意外更改字符串。
- 此外,shell 自身``对双引号字符串的处理可能会造成阻碍;例如,要让*Python*生成文字输出`ro`,必须传递`ro\\b`给它;使用`'...'`shell 字符串和*双重* ``实例,我们得到:
`python -c "$(printf %b 'import sys
print("ro\\bs")')" # ok: 'ros'`
相比之下,这在使用 shell 字符串时*无法*按预期工作`"..."`:
`python -c "$(printf %b "import sys
print('ro\\bs')")" # !! INCORRECT: 'rs'`
shell 将和*都* 解释为文字,需要大量额外的实例才能达到预期的效果:`""``"\\b"`````
`python -c "$(printf %b "import sys
print('ro\\\\bs')")"`
通过而不是传递代码stdin
-c
:
注意:我在这里关注单行解决方案;xorho 的答案展示了如何使用多行here-document - 但一定要引用分隔符;例如<<'EOF'
,除非您明确希望 shell 预先扩展字符串(这带有上面提到的注意事项)。
在bash
、ksh
或 中zsh
:
将ANSI C 引用字符串( $'...'
) 与此处字符串( )组合<<<...
:
python - <<<$'import sys
for r in range(10): print("rob")'
-
明确告诉python
从 stdin 读取(默认情况下会这样做)。-
在这种情况下是可选的,但如果您还想将参数传递给脚本,则需要它来消除脚本文件名中的参数歧义:
python - 'rob' <<<$'import sys
for r in range(10): print(sys.argv[1])'
如果必须保持符合 POSIX 标准:
使用printf
方法如上所述,但使用管道以便通过 stdin 传递其输出:
printf %b 'import sys
for r in range(10): print("rob")' | python
带有论点:
printf %b 'import sys
for r in range(10): print(sys.argv[1])' | python - 'rob'
解决方案 5:
知道如何修复这个问题吗?
您的问题是由以下事实引起的: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)
复合语句不能通过分号与其他语句包含在同一行 - 因此使用-c
标志执行此操作变得非常不方便。
在 bash shell 环境中演示 Python 时,我发现包含复合语句非常有用。唯一简单可靠的方法是使用 heredocs(posix shell 的东西)。
希拉多克
使用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
-
在后面添加<<
( <<-
) 可让您使用制表符进行缩进(Stackoverflow 将制表符转换为空格,因此我缩进了 8 个空格以强调这一点)。 前导制表符将被删除。
您只需使用以下方法即可完成此操作,无需使用标签<<
:
$ python - << "EOF"
import sys
for r in range(10):
print('rob')
EOF
加上引号EOF
可以防止参数和算术扩展。这使得 heredoc 更加健壮。
Bash 多行字符串
如果你使用双引号,你会得到 shell 扩展:
$ python -c "
> import sys
> for p in '$PATH'.split(':'):
> print(p)
> "
/usr/sbin
/usr/bin
/sbin
/bin
...
为了避免 shell 扩展,请使用单引号:
$ 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
...
对已接受答案(及其他答案)的批评
这不太可读:
for 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 循环:
' % x), range(10))"
这些都是令人悲伤和糟糕的事。不要做。
解决方案 6:
只需使用Return
并在下一行输入:
python -c "import sys
for r in range(10): print 'rob'"
rob
rob
...
解决方案 7:
python2.6 -c "import sys; [sys.stdout.write('rob
') for r in range(10)]"
工作正常。
使用“[ ]”内联你的for循环。
解决方案 8:
此变体最具可移植性,适用于在 Windows 和类 Unix 系统、Python 2 和 Python 3 上的命令行上放置多行脚本,无需管道:
python -c "exec(\"import sys
for r in range(10): print('rob') \")"
(到目前为止,这里见到的其它例子均没有这样做。)
Windows 上的 Neat 是:
python -c exec"""import sys
for r in range(10): print 'rob' """
python -c exec("""import sys
for r in range(10): print('rob') """)
类 Unix 系统上的 Bash 的 Neat 功能如下:
python -c $'import sys
for r in range(10): print("rob")'
此函数将任何多行脚本转换为可移植的单行命令:
def py2cmdline(script):
exs = 'exec(%r)' % re.sub('
|
', '
', script.rstrip())
print('python -c "%s"' % exs.replace('"', r'\"'))
用法:
>>> py2cmdline(getcliptext())
python -c "exec('print \'AA A\'
try:
for i in 1, 2, 3:
print i / 0
except:
print \"\"\"longer
message\"\"\"')"
输入是:
print 'AA A'
try:
for i in 1, 2, 3:
print i / 0
except:
print """longer
message"""
解决方案 9:
问题不在于import
语句。问题在于控制流语句无法在 Python 解释器命令中内联运行。将该import
语句替换为任何其他语句,您都会看到同样的问题。
想想看:Python 不可能内联所有内容。它使用缩进来分组控制流。
解决方案 10:
如果您的系统符合 POSIX.2 标准,它应该提供以下printf
实用程序:
printf "print 'zap'
for r in range(3): print 'rob'" | python
zap
rob
rob
rob
解决方案 11:
我并不是一个真正的 Python 爱好者 - 但是我曾经发现过这种语法,忘记从哪里来的了,所以我想记录下来:
如果您使用sys.stdout.write
而不是print
(区别在于,sys.stdout.write
将参数作为函数,放在括号中 - 而print
没有),那么对于单行命令,您可以颠倒命令和 的顺序for
,删除分号,并将命令括在方括号中,即:
python -c "import sys; [sys.stdout.write('rob
') 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 技巧和窍门 – 第一版 - Python 教程 | Dream.In.Code:
如果您还记得的话,单行生成器的标准格式是一种括号内的一行“for”循环。这将生成一个“一次性”可迭代对象,该对象只能在一个方向上迭代,并且一旦到达末尾就无法重复使用。
“列表推导”看起来与常规的单行生成器几乎相同,只是常规括号 - ( ) - 被方括号 - [ ] 取代。列表推导的主要优点是生成一个“列表”,而不是“一次性”可迭代对象,以便您可以来回浏览它、添加元素、排序等。
它确实是一个列表 - 只是它的第一个元素在执行时立即变为无:
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. 数据结构:5.1.4. 列表推导式 — Python v2.7.4 文档中另有说明,内容为“列表推导式提供了一种创建列表的简洁方法”;据推测,这就是列表有限的“可执行性”在单行代码中发挥作用的地方。
好吧,希望我在这里没有偏离主题太远...
下面是一个带有两个非嵌套 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]
请注意,第二个“列表”现在有两个元素,因为它的 for 循环明确运行了两次;但是,两种情况下b
的结果(显然)都是。sys.stdout.write()
`None`
解决方案 12:
single/double quotes
以及backslash
任何地方:
python -c 'exec("import sys
for i in range(10): print \"bob\"")'
好多了:
python -c '
import sys
for i in range(10):
print("bob")
'
注意:在某些系统上,可执行文件python
适用于 Python 2,可能不可用。可执行文件python3
适用于 Python 3。
解决方案 13:
我想要一个具有以下属性的解决方案:
可读性
读取标准输入以处理其他工具的输出
其他答案中没有提供这两个要求,因此这里介绍了在命令行上执行所有操作时读取标准输入的方法:
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:
python -c
与三重引号一起使用:
python -c """
import os
os.system('pwd')
os.system('ls -l')
print('Hello, World!')
for _ in range(5):
print(_)
"""
解决方案 15:
当我需要这样做时,我使用了:
python -c "$(echo -e "import sys
sys.stdout.write('Hello, World!\\
')")"
请注意sys.stdout.write语句中的换行符的三个反斜杠。
解决方案 16:
如果您不想接触标准输入并像传递了“python cmdfile.py”一样进行模拟,您可以从 Bash shell 执行以下操作:
python <(printf "word=raw_input('Enter word: ')
import sys
for i in range(5):
print(word)")
如您所见,它允许您使用标准输入来读取输入数据。在内部,shell 为输入命令内容创建临时文件。
解决方案 17:
还有一个选项。sys.stdout.write返回None,这使列表保持为空:
cat somefile.log|python -c "import sys;[line for line in sys.stdin if sys.stdout.write(line*2)]"