237

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

4

18 回答 18

218

你可以做

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 的回答/ @Crast 的回答

于 2010-01-11T17:18:43.587 回答
107

这种风格也可以在 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 手册页中的此处文档或此处)。

于 2010-03-01T14:38:57.593 回答
65

问题实际上不在于 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执行的小文件不是更好?

于 2010-01-11T17:39:30.960 回答
41


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

bashksh或 中zsh

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

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

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

要将shell 变量值传递给这样的命令,使用参数并通过sys.argvPython 脚本内部访问它们是最安全的:

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命令替换一起使用:

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

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

  • 原始源代码 中的双重\实例。
    • \<char>序列 - 例如\n在这种情况下,以及常见的嫌疑人,如\t, \r, \b- 被扩展printf(参见man printf支持的转义序列)。
  • 将单引号字符串传递给嵌入的单引号printf %b并将其转义为(sic)。 '\''

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

      • 也就是说,对于简短的Python 脚本(如本例所示),您可以使用双引号字符串将shell变量值合并到您的脚本中——只要您知道相关的陷阱(请参阅下一点);例如,shell 扩展$HOME为当前用户的主目录。在以下命令中:

        • python -c "$(printf %b "import sys\nfor r in range(10): print('rob is $HOME')")"
      • 但是,通常首选的方法是通过参数sys.argv从 shell 传递值,并通过Python访问它们;上述命令的等价物是:

        • 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"literal "\\b"\b需要令人眼花缭乱的额外\实例才能达到预期的效果:
        python -c "$(printf %b "import sys\nprint('ro\\\\\\\\bs')")"

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

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


bashksh或 中zsh

ANSI C 引用的字符串( $'...') 与此处的字符串( <<<...) 组合起来:

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

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

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'
于 2015-04-10T15:44:56.860 回答
32

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

您的问题是由以下事实造成的:由 分隔的 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 的东西)。

Heredocs

使用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

添加-after <<(the <<-) 允许您使用制表符来缩进(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
...

为避免外壳扩展,请使用单引号:

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

这些都是可悲和不好的。不要做他们。

于 2016-07-06T23:56:34.410 回答
16

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

user@host:~$ python -c "import sys
> for r in range(10): print 'rob'"
rob
rob
...
于 2010-01-11T17:14:19.760 回答
11

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

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

于 2012-01-11T13:40:49.527 回答
8

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

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

于 2010-01-11T17:19:27.240 回答
7

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

$ printf "print 'zap'\nfor r in range(3): print 'rob'" | python
zap
rob
rob
rob
于 2010-01-12T01:33:45.763 回答
5

此变体最便于在 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"""
于 2016-02-26T12:32:40.750 回答
4

(2010 年 11 月 23 日 19:48 回答) 我并不是一个真正的 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 中调用此语法 :)

希望这可以帮助,

干杯!


(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 - 第一版 - Python 教程 | Dream.In .代码:

如果您还记得,单行生成器的标准格式是括号内的一种单行“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. 数据结构:5.1.4中另有说明。List Comprehensions — Python v2.7.4 文档作为“列表理解提供了一种创建列表的简洁方法”;据推测,这就是列表的有限“可执行性”在单行中发挥作用的地方。

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

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

于 2010-11-23T19:48:43.007 回答
4

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"
> '
于 2011-12-11T08:17:12.023 回答
2

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

Pyliner - 在命令行上运行任意 Python 代码的脚本(Python 配方)

于 2012-02-16T18:37:15.817 回答
2

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

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

其他答案中未提供这两个要求,因此这是在命令行上执行所有操作时如何读取标准输入的方法:

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
)
于 2018-11-27T18:32:08.803 回答
1

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

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

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

于 2013-01-10T14:06:22.270 回答
1

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

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

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

于 2013-02-14T11:11:33.080 回答
1

使用带有三引号的 python -c

python -c """
import os
os.system('pwd')
os.system('ls -l')
print('Hello World!')
for _ in range(5):
    print(_)
"""
于 2021-02-19T02:05:29.693 回答
0

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

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

于 2014-06-06T04:22:39.997 回答