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.
If your system is Posix.2 compliant it should supply the
printf
utility:single/double quotes
andbackslash
everywhere:Much better:
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 thatimport
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.
This script provides a Perl-like command line interface:
Pyliner - Script to run arbitrary Python code on the command line (Python recipe)
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:
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.
- To make this answer work with Python 3.x as well,
print
is called as a function: in 3.x, onlyprint('foo')
works, whereas 2.x also acceptsprint 'foo'
.- For a cross-platform perspective that includes Windows, see kxr's helpful answer.
In
bash
,ksh
, orzsh
:Use an ANSI C-quoted string (
$'...'
), which allows using\n
to represent newlines that are expanded to actual newlines before the string is passed topython
:Note the
\n
between theimport
andfor
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: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:\
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$'...'
(seeman printf
for the supported escapes)'
instances as\'
.If you must remain POSIX-compliant:
Use
printf
with a command substitution:To work safely with this type of string:
\
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 byprintf
(seeman 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.\
processing in double-quoted strings can get in the way; for instance, to get Python to produce literal outputro\b
, you must passro\\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
, orzsh
:Combine an ANSI C-quoted string (
$'...'
) with a here-string (<<<...
):-
tellspython
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:If you must remain POSIX-compliant:
Use
printf
as above, but with a pipeline so as to pass its output via stdin:With an argument: