问题:在单行命令行中执行多行语句?

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

I’m using Python with -c to execute a one-liner loop, i.e.:

$ python -c "for r in range(10): print 'rob'"

This works fine. However, if I import a module before the for loop, I get a syntax error:

$ 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

Any idea how this can be fixed?

It’s important to me to have this as a one-liner so that I can include it in a Makefile.


回答 0

你可以做

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的答案

you could do

echo -e "import sys\nfor r in range(10): print 'rob'" | python

or w/out pipes:

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

or

(echo "import sys" ; echo "for r in range(10): print 'rob'") | python

or @SilentGhost’s answer / @Crast’s answer


回答 1

这种样式也可以在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可以忍受行首未出现在here文档中的任何标记词(另请参见bash联机帮助页中的here文档或here)。

this style can be used in makefiles too (and in fact it is used quite often).

python - <<EOF
import sys
for r in range(3): print 'rob'
EOF

or

python - <<-EOF
    import sys
    for r in range(3): print 'rob'
EOF

in latter case leading tab characters are removed too (and some structured outlook can be achieved)

instead of EOF can stand any marker word not appearing in the here document at a beginning of a line (see also here documents in the bash manpage or here).


回答 2

问题实际上与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))"

但是,lambda只能执行表达式,而不能执行语句或多个语句,因此您可能仍然无法执行想要执行的操作。但是,在生成器表达式,列表推导,lambda,sys.stdout.write,内置的“ map”以及一些创造性的字符串插值之间,您可以执行一些强大的单线操作。

问题是,您想走多远?在什么时候写一个.py由makefile执行的小文件更好?

The issue is not actually with the import statement, it’s with anything being before the for loop. Or more specifically, anything appearing before an inlined block.

For example, these all work:

python -c "import sys; print 'rob'"
python -c "import sys; sys.stdout.write('rob\n')"

If import being a statement were an issue, this would work, but it doesn’t:

python -c "__import__('sys'); for r in range(10): print 'rob'"

For your very basic example, you could rewrite it as this:

python -c "import sys; map(lambda x: sys.stdout.write('rob%d\n' % x), range(10))"

However, lambdas can only execute expressions, not statements or multiple statements, so you may still be unable to do the thing you want to do. However, between generator expressions, list comprehension, lambdas, sys.stdout.write, the “map” builtin, and some creative string interpolation, you can do some powerful one-liners.

The question is, how far do you want to go, and at what point is it not better to write a small .py file which your makefile executes instead?


回答 3


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

bashkshzsh

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

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

注意和语句\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"

请参阅以下内容,讨论使用带嵌入式外壳变量引用的(转义序列预处理的)双引号命令字符串的优缺点。

为了安全地使用$'...'字符串:

  • \您的原始源代码中的实例。
    • \<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所支持的转义序列)。
  • 单引号字符串传递给(sic)printf %b转义嵌入的单引号 '\''

    • 使用单引号可以防止shell解释字符串的内容。

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

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

        • python -c "$(printf %b 'import sys\nfor r in range(10): print("rob is " + sys.argv[1])')" "$HOME"
    • 使用双引号的字符串更方便 -它使您可以使用未转义的嵌入式单引号和嵌入式双引号\"–它还使字符串受外壳程序解释,这可能是或不是意图;$`在源代码字符不是用于外壳可能会导致语法错误或意外改变的字符串。

      • 另外,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')")"

,而不是-c

注意:我在这里专注于单线解决方案。xorho的答案显示了如何使用多行here-document-但是请务必引用定界符;例如,,<<'EOF'除非您明确希望外壳程序将字符串扩展到前面(上述注意事项附带)。


bashkshzsh

结合使用ANSI C引号的字符串$'...')和here字符串<<<...):

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如上使用,但使用管道以便通过stdin传递其输出:

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'


– To make this answer work with Python 3.x as well, print is called as a function: in 3.x, only print('foo') works, whereas 2.x also accepts print 'foo'.
– For a cross-platform perspective that includes Windows, see kxr’s helpful answer.

In bash, ksh, or zsh:

Use an ANSI C-quoted string ($'...'), which allows using \n to represent newlines that are expanded to actual newlines before the string is passed to python:

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

Note the \n between the import and for statements to effect a line break.

To pass shell-variable values to such a command, it is safest to use arguments and access them via sys.argv inside the Python script:

name='rob' # value to pass to the Python script
python -c $'import sys\nfor r in range(10): print(sys.argv[1])' "$name"

See below for a discussion of the pros and cons of using an (escape sequence-preprocessed) double-quoted command string with embedded shell-variable references.

To work safely with $'...' strings:

  • Double \ instances in your original source code.
    • \<char> sequences – such as \n in this case, but also the usual suspects such as \t, \r, \b – are expanded by $'...' (see man printf for the supported escapes)
  • Escape ' instances as \'.

If you must remain POSIX-compliant:

Use printf with a command substitution:

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

To work safely with this type of string:

  • Double \ instances in your original source code.
    • \<char> sequences – such as \n in this case, but also the usual suspects such as \t, \r, \b – are expanded by printf (see man printf for the supported escape sequences).
  • Pass a single-quoted string to printf %b and escape embedded single quotes as '\'' (sic).

    • Using single quotes protects the string’s contents from interpretation by the shell.

      • That said, for short Python scripts (as in this case) you can use a double-quoted string to incorporate shell variable values into your scripts – as long as you’re aware of the associated pitfalls (see next point); e.g., the shell expands $HOME to the current user’s home dir. in the following command:

        • python -c "$(printf %b "import sys\nfor r in range(10): print('rob is $HOME')")"
      • However, the generally preferred approach is to pass values from the shell via arguments, and access them via sys.argv in Python; the equivalent of the above command is:

        • python -c "$(printf %b 'import sys\nfor r in range(10): print("rob is " + sys.argv[1])')" "$HOME"
    • While using a double-quoted string is more convenient – it allows you to use embedded single quotes unescaped and embedded double quotes as \" – it also makes the string subject to interpretation by the shell, which may or may not be the intent; $ and ` characters in your source code that are not meant for the shell may cause a syntax error or alter the string unexpectedly.

      • Additionally, the shell’s own \ processing in double-quoted strings can get in the way; for instance, to get Python to produce literal output ro\b, you must pass ro\\b to it; with a '...' shell string and doubled \ instances, we get:
        python -c "$(printf %b 'import sys\nprint("ro\\\\bs")')" # ok: 'ro\bs'
        By contrast, this does not work as intended with a "..." shell string:
        python -c "$(printf %b "import sys\nprint('ro\\\\bs')")" # !! INCORRECT: 'rs'
        The shell interprets both "\b" and "\\b" as literal \b, requiring a dizzying number of additional \ instances to achieve the desired effect:
        python -c "$(printf %b "import sys\nprint('ro\\\\\\\\bs')")"

To rather than -c:

Note: I’m focusing on single-line solutions here; xorho’s answer shows how to use a multi-line here-document – be sure to quote the delimiter, however; e.g., <<'EOF', unless you explicitly want the shell to expand the string up front (which comes with the caveats noted above).


In bash, ksh, or zsh:

Combine an ANSI C-quoted string ($'...') with a here-string (<<<...):

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

- tells python explicitly to read from stdin (which it does by default). - is optional in this case, but if you also want to pass arguments to the scripts, you do need it to disambiguate the argument from a script filename:

python - 'rob' <<<$'import sys\nfor r in range(10): print(sys.argv[1])'

If you must remain POSIX-compliant:

Use printf as above, but with a pipeline so as to pass its output via stdin:

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

With an argument:

printf %b 'import sys\nfor r in range(10): print(sys.argv[1])' | python - 'rob'

回答 4

任何想法如何解决?

您的问题是由以下事实引起的:用分隔的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

使用定界符(与创建<<)和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 <<<<-)允许您使用制表符缩进(Stackoverflow将制表符转换为空格,因此我缩进了8个空格以强调这一点)。前导标签将被剥离。

您只需使用以下选项卡就可以做到<<

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

用引号引起来EOF可防止参数算术扩展。这使heredoc更加健壮。

重击多行字符串

如果使用双引号,则会得到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
...

批评接受的答案(和其他)

这不是很可读:

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或列出理解以获得循环:

python -c "import sys; map(lambda x: sys.stdout.write('rob%d\n' % x), range(10))"

这些都是悲伤和坏的。不要做

Any idea how this can be fixed?

Your problem is created by the fact that Python statements, separated by ;, are only allowed to be “small statements”, which are all one-liners. From the grammar file in the 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)

Compound statements can’t be included on the same line with other statements via semicolons – so doing this with the -c flag becomes very inconvenient.

When demonstrating Python while in a bash shell environment, I find it very useful to include compound statements. The only simple way of doing this reliably is with heredocs (a posix shell thing).

Heredocs

Use a heredoc (created with <<) and Python’s 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

Adding the - after << (the <<-) allows you to use tabs to indent (Stackoverflow converts tabs to spaces, so I’ve indented 8 spaces to emphasize this). The leading tabs will be stripped.

You can do it without the tabs with just <<:

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

Putting quotes around EOF prevents parameter and arithmetic expansion. This makes the heredoc more robust.

Bash multiline strings

If you use double-quotes, you’ll get shell-expansion:

$ python -c "
> import sys
> for p in '$PATH'.split(':'):
>     print(p)
> "
/usr/sbin
/usr/bin
/sbin
/bin
...

To avoid shell expansion use single-quotes:

$ python -c '
> import sys
> for p in "$PATH".split(":"):
>     print(p)
> '
$PATH

Note that we need to swap the quote characters on the literals in Python – we basically can’t use quote character being interpreted by BASH. We can alternate them though, like we can in Python – but this already looks quite confusing, which is why I don’t recommend this:

$ python -c '
import sys
for p in "'"$PATH"'".split(":"):
    print(p)
'
/usr/sbin
/usr/bin
/sbin
/bin
...

Critique of the accepted answer (and others)

This is not very readable:

echo -e "import sys\nfor r in range(10): print 'rob'" | python

Not very readable, and additionally difficult to debug in the case of an error:

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

Perhaps a bit more readable, but still quite ugly:

(echo "import sys" ; echo "for r in range(10): print 'rob'") | python

You’ll have a bad time if you have "‘s in your python:

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

Don’t abuse map or list comprehensions to get for-loops:

python -c "import sys; map(lambda x: sys.stdout.write('rob%d\n' % x), range(10))"

These are all sad and bad. Don’t do them.


回答 5

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

user@host:~$ python -c "import sys
> for r in range(10): print 'rob'"
rob
rob
...

just use return and type it on the next line:

user@host:~$ python -c "import sys
> for r in range(10): print 'rob'"
rob
rob
...

回答 6

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

工作良好。使用“ []”内联for循环。

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

Works fine. Use “[ ]” to inline your for loop.


回答 7

问题不在于import语句。问题在于控制流语句无法在python命令中内联。用import其他任何语句替换该语句,您将看到相同的问题。

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

The problem is not with the import statement. The problem is that the control flow statements don’t work inlined in a python command. Replace that import statement with any other statement and you’ll see the same problem.

Think about it: python can’t possibly inline everything. It uses indentation to group control-flow.


回答 8

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

$ printf "print 'zap'\nfor r in range(3): print 'rob'" | python
zap
rob
rob
rob

If your system is Posix.2 compliant it should supply the printf utility:

$ printf "print 'zap'\nfor r in range(3): print 'rob'" | python
zap
rob
rob
rob

回答 9

single/double quotesbackslash无处不在:

$ python -c 'exec("import sys\nfor i in range(10): print \"bob\"")'

好多了:

$ python -c '
> import sys
> for i in range(10):
>   print "bob"
> '

single/double quotes and backslash everywhere:

$ python -c 'exec("import sys\nfor i in range(10): print \"bob\"")'

Much better:

$ python -c '
> import sys
> for i in range(10):
>   print "bob"
> '

回答 10

(在10年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 EDIT)好吧,我想我终于找到了这些单线括起来的方括号。它们是“列表理解”(显然);首先请注意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()即可执行命令,它会在分配后立即执行;但是,以后的检查发现aNone

$ 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文档为“列表推导提供了一种创建列表的简洁方法”;大概就是列表的有限“可执行性”发挥作用的地方。

好吧,希望我在这里不会太过分……

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

(answered Nov 23 ’10 at 19:48) I’m not really a big Pythoner – but I found this syntax once, forgot where from, so I thought I’d document it:

if you use sys.stdout.write instead of print (the difference being, sys.stdout.write takes arguments as a function, in parenthesis – whereas print doesn’t), then for a one-liner, you can get away with inverting the order of the command and the for, removing the semicolon, and enclosing the command in square brackets, i.e.:

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

Have no idea how this syntax would be called in Python :)

Hope this helps,

Cheers!


(EDIT Tue Apr 9 20:57:30 2013) Well, I think I finally found what these square brackets in one-liners are about; they are “list comprehensions” (apparently); first note this in 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>

So the command in round brackets/parenthesis is seen as a “generator object”; if we “iterate” through it by calling next() – then the command inside the parenthesis will be executed (note the “abc” in the output):

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

If we now use square brackets – note that we don’t need to call next() to have the command execute, it executes immediately upon assignment; however, later inspection reveals that a is None:

$ echo $STR | python -c "import sys,re; a=[sys.stdout.write(line) for line in sys.stdin]; print a"
abc
[None]

This doesn’t leave much info to look for, for the square brackets case – but I stumbled upon this page which I think explains:

Python Tips And Tricks – First Edition – Python Tutorials | Dream.In.Code:

If you recall, the standard format of a single line generator is a kind of one line ‘for’ loop inside brackets. This will produce a ‘one-shot’ iterable object which is an object you can iterate over in only one direction and which you can’t re-use once you reach the end.

A ‘list comprehension’ looks almost the same as a regular one-line generator, except that the regular brackets – ( ) – are replaced by square brackets – [ ]. The major advanatge of alist comprehension is that produces a ‘list’, rather than a ‘one-shot’ iterable object, so that you can go back and forth through it, add elements, sort, etc.

And indeed it is a list – it’s just its first element becomes none as soon as it is executed:

$ 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

List comprehensions are otherwise documented in 5. Data Structures: 5.1.4. List Comprehensions — Python v2.7.4 documentation as “List comprehensions provide a concise way to create lists”; presumably, that’s where the limited “executability” of lists comes into play in one-liners.

Well, hope I’m not terribly too off the mark here …

EDIT2: and here is a one-liner command line with two non-nested for-loops; both enclosed within “list comprehension” square brackets:

$ 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]

Notice that the second “list” b now has two elements, since its for loop explicitly ran twice; however, the result of sys.stdout.write() in both cases was (apparently) None.


回答 11

此变体最便于移植,用于在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"""

This variant is most portable for putting multi-line scripts on command-line on Windows and *nix, py2/3, without pipes:

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

(None of the other examples seen here so far did so)

Neat on Windows is:

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/*nix is:

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

This function turns any multiline-script into a portable command-one-liner:

def py2cmdline(script):
    exs = 'exec(%r)' % re.sub('\r\n|\r', '\n', script.rstrip())
    print('python -c "%s"' % exs.replace('"', r'\"'))

Usage:

>>> 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\"\"\"')"

Input was:

print 'AA   A'
try:
 for i in 1, 2, 3:
  print i / 0
except:
 print """longer
message"""

回答 12

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

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


回答 13

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

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

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

When I needed to do this, I use

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

Note the triple backslash for the newline in the sys.stdout.write statement.


回答 14

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

  1. 可读的
  2. 阅读stdin来处理其他工具的输出

其他答案中未同时提供这两个要求,因此,这是在命令行上执行所有操作时如何读取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
)

I wanted a solution with the following properties:

  1. Readable
  2. Read stdin for processing output of other tools

Both requirements were not provided in the other answers, so here’s how to read stdin while doing everything on the command line:

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
)

回答 15

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

cat somefile.log | python -c“ import sys; [如果sys.stdout.write(line * 2),则为sys.stdin中的行的一行]”

there is one more option, sys.stdout.write returns None, which keep the list empty

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

回答 16

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

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

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

If you don’t want to touch stdin and simulate as if you had passed “python cmdfile.py”, you can do the following from a bash shell:

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

As you can see, it allows you to use stdin for reading input data. Internally the shell creates the temporary file for the input command contents.


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。