Executing Python multi-line statements in the one-

2019-01-03 07:54发布

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.

18条回答
三岁会撩人
2楼-- · 2019-01-03 08:33

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"""
查看更多
家丑人穷心不美
3楼-- · 2019-01-03 08:35

I've written a simple web page for this. You can paste your multiple-line code there and it is converted into a working single-line statement.

Python Single Line Converter

查看更多
成全新的幸福
4楼-- · 2019-01-03 08:38

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 is with heredocs.

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.

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楼-- · 2019-01-03 08:40

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

查看更多
Bombasti
6楼-- · 2019-01-03 08:41

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
...
查看更多
手持菜刀,她持情操
7楼-- · 2019-01-03 08:42

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).

查看更多
登录 后发表回答