views:

723

answers:

6

I'm using python -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?

A: 

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
...
SilentGhost
It's important to me to have this as a one-liner so that I can include it in a Makefile.
You can put this in a Makefile by using backward slashes to make it span multiple lines but it's going to be a nightmare later. Why not use something more tuned for one liners like Perl or Awk?
Noufal Ibrahim
Seriously, you're going to sprain something if you keep doing this. `python $(srcdir)/myscript.py` for great justice.
Jason Orendorff
You're not helping the cause, I was trying to convert my friend from Perl to Python and he complained that these one-liners don't work in Python :).
@OP: of course they don't! that's not how python should be used.
SilentGhost
You'll end up in a mess if you try to write X code in Y language when X!=Y. Perl and Python are different philosophically and trying to emulate one in the other is a waste of time and totally useless. It's better you spend time in learning their strengths and code idiomatically rather than focussing on tricks.
Noufal Ibrahim
unknown (google): Right, your mistake was trying to engage him on that point. You should have taken a different tack: "OK, but a lot of programs are more than one line long..."
Jason Orendorff
+6  A: 

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

jspcal
This works great! Using this answer, I was able to write a one-liner that shows all of the RPMs installed today on a RHEL5 system. The python bit is necessary to reverse the lines: rpm -qa --last | sed -n "/ $(date +'%a %e %b %Y')/s/ .*//p" | python -c "exec('''import fileinput\nx=[]\nfor l in fileinput.input(): x.append(l)\nx.reverse()\nfor l in x: print l,''')"
JohnnyLambada
+5  A: 

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.

David Berger
+1  A: 

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?

Crast
+2  A: 

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
Alex Martelli
+1  A: 

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

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

or

cat <<-EOF | python -
    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).

xorho