在单行命令行中执行多行语句

我使用Python和-c来执行一行循环,即:

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

这很好。但是,如果我在循环之前导入一个模块,就会得到一个语法错误:

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中。

199141 次浏览

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

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


rob
rob
...

你可以这样做

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

问题不在于import语句。问题是控制流语句不能内联在Python解释器命令中工作。将import语句替换为任何其他语句,你会看到同样的问题。

想想看:Python不可能内联所有东西。它使用缩进来分组控制流。

问题并不在于进口语句。它是在循环之前的任何东西。或者更具体地说,出现在内联块之前的任何东西。

例如,这些都是有效的:

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执行不是更好?

如果你的系统是posix .2兼容的,它应该提供printf实用程序:

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


zap
rob
rob
rob

这种风格也可以在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.

在上述情况下,前导制表符也被删除(并且可以实现一些结构化的外观)。

EOF可以容忍任何不在这里文档中出现在一行开头的标记词(另见bash手册页中的文档或在这里)。

我不是一个真正的大python -但我发现这个语法一次,忘记从哪里来的,所以我想我应该记录它:

如果你使用sys.stdout.write而不是print (区别在于,__ABC0接受圆括号中的函数参数,而print不接受),那么对于一行程序,你可以将命令和for的顺序颠倒,删除分号,并将命令括在方括号中,即:

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

我不知道这个语法在Python中是如何调用的:)


一行中的方括号是"list comprehension ";在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”循环。这将产生一个“one-shot”可迭代对象,该对象只能在一个方向上迭代,一旦到达终点就不能重用。

“列表推导式”看起来几乎与常规的单行生成器相同,除了常规的括号-()-被方括号-[]取代。alist comprehension的主要优点是它生成了一个“列表”,而不是一个“一次性”的可迭代对象,这样你就可以来回遍历它,添加元素,排序等等。

实际上它是一个列表——只是它的第一个元素在执行时变成了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。列表推导式- Python v2.7.4文档中有另一种记录,“列表推导式提供了一种创建列表的简明方法”;;大概,这就是为什么有限的“可执行性”;Of列表在一行语句中发挥作用。

好吧,希望我没有说得太离谱……

这里有一个单行命令行,包含两个非嵌套的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 quotesbackslash无处不在:

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。

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

工作很好。

使用"[]"inline你的循环。

这个脚本提供了一个类似perl的命令行接口:

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

如果你不想触摸标准输入并模拟你已经传递了"python cmdfile.py",你可以在Bash shell中执行以下操作:

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

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

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

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

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

还有一个选择。sys.stdout.write返回没有一个,保持列表为空:

cat somefile.log|python -c "import sys;[line for line in sys.stdin if sys.stdout.write(line*2)]"
< p > <一口> < br > -为了使这个答案也适用于Python 3.倍print被称为函数:在3中。x, 只有 print('foo')工作,而2。x也接受print 'foo'.
. x —包含窗户的跨平台透视图,请参见Kxr的回答很有帮助.

__ABC0, __ABC1,或zsh:

使用ANSI c -quote string ($'...'),它允许使用\n表示在字符串传递给python之前展开为实际换行的换行:

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

注意importfor语句之间的\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变量引用的优点和缺点。

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

  • 你的原始源代码中的\实例。
    • \<char>序列——比如本例中的\n,还有一些常见的可疑序列,比如\t\r\b——由$'...'扩展(支持的转义参见man printf)
    • 李< / ul > < / >
    • '实例转义为\'

    如果你必须保持posix兼容:

    使用printf命令替换:

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

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

    • 你的原始源代码中的\实例。
      • \<char>序列——比如本例中的\n,还有一些常见的可疑序列,比如\t\r\b——由printf扩展(支持的转义序列请参阅man printf)。
      • 李< / ul > < / >
      • 使用单引号字符串传递给printf %b转义嵌入的单引号为 '\'' (sic)。

        • 使用单引号可以保护字符串的内容不被壳牌

          • 也就是说,对于 Python脚本(就像在这种情况下),你可以使用双引号字符串将壳牌变量值合并到你的脚本中-只要你意识到相关的陷阱(见下一点);例如,shell将$HOME扩展到当前用户的主目录。执行如下命令:

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

              • python -c "$(printf %b 'import sys\nfor r in range(10): print("rob is " + sys.argv[1])')" "$HOME"
              • 李< / ul > < / > 李< / ul > < / >
              • 虽然使用双引号字符串更像是方便——它允许你使用未转义的嵌入单引号和嵌入双引号作为\"——它也使字符串受到壳牌的解释,这可能是也可能不是目的;源代码中不用于shell的$`字符可能会导致语法错误或意外地改变字符串。

                  此外,shell自己在双引号字符串中的\处理可能会碍事;例如,为了让Python产生文字输出ro\b,你必须将ro\\b传递给它;'...' shell字符串和翻了一倍 \实例,我们得到:
                  python -c "$(printf %b 'import sys\nprint("ro\\\\bs")')" # ok: 'ro\bs' < br > 相比之下,它使用"..." shell字符串执行工作:
                  python -c "$(printf %b "import sys\nprint('ro\\\\bs')")" # !! INCORRECT: 'rs' < br > shell将这两个 "\b""\\b"解释为字面\b,需要大量额外的\实例才能达到预期的效果:
                  python -c "$(printf %b "import sys\nprint('ro\\\\\\\\bs')")" < / > 李< / ul > < / > 李< / ul > < / >

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

                注意:我关注的是-线解决方案;xorho的回答展示了如何使用多行here-document -一定要quote分隔符,然而;例如,<<'EOF',除非你显式地希望shell在前面展开字符串(这与上面提到的注意事项一起出现)。


                __ABC0, __ABC1,或zsh:

                ANSI c -quote string ($'...')与 (<<<...)组合:

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

                -显式地告诉python从stdin读取(它默认这样做)。 在这种情况下,-是可选的,但如果你还想将参数传递给脚本,你确实需要它来消除脚本文件名的参数歧义:

                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'
                

这个变体在Windows和类unix系统(Python 2和Python 3)的命令行上放置多行脚本是最可移植的,没有管道:

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

(到目前为止,这里看到的其他例子都没有这样做。)

Windows上的Neat是:

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

类unix系统上Bash的Neat是:

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

你知道怎么解决吗?

你的问题是由这样一个事实造成的: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

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

$ 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或列表推导式来获取for-loops:

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

这些都是悲伤而糟糕的。不要这样做。

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

  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
)

使用带有三引号的python -c:

python -c """
import os
os.system('pwd')
os.system('ls -l')
print('Hello, World!')
for _ in range(5):
print(_)
"""