Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Executing multi-line statements in the one-line command-line?

People also ask

How do you run multiple lines in command prompt?

The Windows command prompt (cmd.exe) allows the ^ (Shift + 6) character to be used to indicate line continuation. It can be used both from the normal command prompt (which will actually prompt the user for more input if used) and within a batch file.

What is Multiline command?

Command input may span multiple lines for the commands whose names are listed in the multiline_commands argument to cmd2. Cmd. __init__() . These commands will be executed only after the user has entered a terminator.

How do I make multiple lines in one line in Python?

You can have a string split across multiple lines by enclosing it in triple quotes. Alternatively, brackets can also be used to spread a string into different lines. Moreover, backslash works as a line continuation character in Python. You can use it to join text on separate lines and create a multiline string.

How do you write multiple lines in Linux?

To add multiple lines to a file with echo, use the -e option and separate each line with \n. When you use the -e option, it tells echo to evaluate backslash characters such as \n for new line. If you cat the file, you will realize that each entry is added on a new line immediately after the existing content.


You could do

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

or without 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


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


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?



- 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 pass the code via stdin 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'

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.


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