How to run multiple lines using python -c - python

I need to use python -c to remotely run some code, it works when I use:
python -c "a=4;print a"
4
Or
python -c "if True:print 'ye'"
But python -c "a=4;if a<5:print 'ye'" will generate an error:
File "<string>", line 1
a=4;if a<5:print 'ye'
SyntaxError: invalid syntax
What should I do to make it work, any advice?

Enclose it in single quotes and use multiple lines:
python -c '
a = 4
if a < 5:
print "ye"
'
If you need a single quote in the code, use this horrible construct:
python -c '
a = 4
if a < 5:
print '\''ye'\''
'
This works because most UNIX shells will not interpret anything between single quotes—so we have some Python code right up until where we need a single quote. Since it’s completely uninterpreted, we can’t just escape the quote; rather, we end the quotation, then insert a literal quotation mark (escaped, so it won’t be interpreted as the start of a new quote), and finally another quote to put us back into the uninterpreted-string state. It’s a little ugly, but it works.

Related

How to ignore the double quotes while concatenating a number as a string in between a string using python?

I have a variable "cnt" which gets incremented in every iteration and I am concatenating the value to a string but I dont want the double quotes .
For example:
cnt=1234
str1="tti -c "+'"something,MID="'+str(cnt)+'",ID=Udm5gIgAf1;"'
When I am printing str1,I am getting output as below
tti -c "something,MID="1234",ID=Udm5gIgAf1;"
But I want like
tti -c "something,MID=1234,ID=Udm5gIgAf1;"
I'm pretty sure those double quotes are because you have them as characters in your string
try
str1="tti -c "+'"something,MID='+str(cnt)+',ID=Udm5gIgAf1;"'
str1="tti -c "+'"something,MID='+str(cnt)+',ID=Udm5gIgAf1;"'
You have double-quotes surrounding your other pieces of the string, not around your number. So remove those as I wrote it above.
However, why do you even have the concatenating of the second piece here at all? Why not just str1="ttti -c "something, MID=' + str(cnt) + ',ID=blahblah;"'
Better yet, use f-strings:
string1 = f'tti -c "something,MID={str(cnt)},ID=Udm5gIgAf1;"'
You're adding the double quotes. They can be removed. You may want to consider using an f-string.
This will allow you to modify the dynamic parts of your command without rewriting the static parts, every time.
Below are example string formatting techniques based on your command.
#notice that `MID` is never explicitly cast to `str`
args = ('something', 1234, 'Udm5gIgAf1')
#f-string
command = lambda a,b,c: f'tti -c "{a},MID={b},ID={c};"'
cmd = command(*args)
#LEGACY ALTERNATIVES
command = 'tti -c "%s,MID=%i,ID=%s;"'
cmd = command % args
command = 'tti -c "{},MID={},ID={};"'
cmd = command.format(*args)
Make sure to use remove them from the string! After take a look and they should have disappeared.
Anyhow the use of f-string will make your life easier
f'tti -c \"something,MID={cnt},ID=Udm5gIgAf1;\"'
or
f'''tti -c "something,MID={cnt},ID=Udm5gIgAf1;"'''

How can I use ssh with print of python?

I would like to run ssh with print of python.
The followings are my test code.
import subprocess
# case1:
command_str = "\"print(\'test\')\""
# case 2:
# command_str = "\\\"print(\'test\')\\\""
ssh_command = ['ssh', 'USER_X#localhost', 'python', '-c']
ssh_command.append(command_str)
process = subprocess.run(ssh_command, stdout=subprocess.PIPE)
print(process.stdout)
case 1 and case 2 did not work.
The outputs are followings,
case 1:
bash: -c: line 0: syntax error near unexpected token `('
bash: -c: line 0: `python -c print('test')'
b''
case 2:
bash: -c: line 0: syntax error near unexpected token `('
bash: -c: line 0: `python -c \"print('test')\"'
b''
Please let me know how it works.
It should work with
command_str = "'print(\"test\")'"
or equivalently
command_str = '\'print("test")\''
Explanation
The outermost quotes and the escaping are for the local Python. So in either case, the local Python string will be 'print("test")'.
There is no quoting or escaping required for the local shell, as subcommand.run(...) won't invoke it unless shell=True is passed.
Thus the single quotes within the python string are for the remote shell (presumably bash or other sh-compatible shell). The argument passed to the remote Python is thus print("test"). (And the double quotes in there are to signify the string literal to print to the remote python.)
Can we do without escaping (without \)?
As there are three levels involved (local Python, remote shell, remote Python), I don't think so.
Can we do with a single type of quotes?
Yes, with a bit more escaping. Let's build this from behind (or inside-out).
We want to print
test
This needs to be escaped for the remote Python (to form a string literal instead of an identifier):
"test"
Call this with the print() function:
print("test")
Quite familiar so far.
Now we want to pass this as an argument to python -c on a sh-like shell. To protect the ( and ) to be interpreted by that, we quote the whole thing. For the already present " not to terminate the quotation, we escape them:
"print(\"test\")"
You can try this in a terminal:
$> echo "print(\"test\")"
print("test")
Perfect!
Now we have to represent the whole thing in (the local) Python. We wrap another layer of quotes around it, have to escape the four(!) existing quotation marks as well as the two backslashes:
"\"print(\\\"test\\\")\""
(Done. This can also be used as command_str.)
Can we do with only single quotes (') and escaping?
I don't know, but at least not as easily. Why? Because, other than to Python, double and single quotes aren't interchangeable to sh and bash: Within single quotes, these shells assume a raw string without escaping until the closing ' occurs.
My brain hurts!
If literally, go see a doctor. If figuratively, yeah, mine too. And your code's future readers (including yourself) will probably feel the same, when they try to untangle that quoting-escaping-forest.
But there's a painless alternative in our beloved Python standard library!
import shlex
command_str = shlex.quote('print("test")')
This is much easier to understand. The inner quotes (double quotes here, but doesn't really matter: shlex.quote("print('test')") works just as fine) are for the remote Python. The outer quotes are obviously for the local Python. And all the quoting and escaping beyond that for the remote shell is taken care of by this utility function.
The correct syntax for python 2 and 3 is:
python -c 'print("test")'

python multiline command running from bash

I'm trying to run this:
python -c "for i in range(10):\n print i"
but I get an error:
File "<string>", line 1
for i in range(10):\n print i
^
SyntaxError: unexpected character after line continuation character
According to this I assume that bash should have processed (namely, newline symbol) command line arguments but the returned error shows the opposite case. Where am I wrong, and why does this happen?
P.S. python-2.7
EDIT
Let me explain my motivation a bit.
This code example is definitely pretty silly. Since the doc says that "command can be one or more statements separated by newlines, with significant leading whitespace as in normal module code", I was interested in how should I bring those mentioned newlines to the command properly.
The proposed solutions here are:
Use ; to distinguish several commands inside the loop. Yes, that works but it still is a one-liner, I can not use it If I want to run some commands after the loop. ; is not a replacement for a newline.
Type ^M where newline is needed. This hits the goal more precisely but unfortunately, to my point of view, this basically ruins the whole idea of running a python code from the command line because it requires interactive mode. As I understand it's the same as entering a command ant hitting Enter key. So no difference to typing python and working in its shell. That said, I cannot write this in a bash script. Or may I?
Probably the question really should have been splitted into two ones:
Bash escaping:
Enclosing characters in double quotes (‘"’) preserves the literal value of all characters within the quotes, with the exception of ‘$’, ‘’, ‘\’, and, when history expansion is enabled, ‘!’. The characters ‘$’ and ‘’ retain their special meaning within double quotes (see Shell Expansions). The backslash retains its special meaning only when followed by one of the following characters: ‘$’, ‘`’, ‘"’, ‘\’, or newline.
How does this correspond to the case described? How does bash handles newlines? I found that putting the command into unary quotes makes no change.
How to pass a newline to python in a non-interactive way. (You may say -- why don't you write an ordinary python file with all newlines you want -- you are right but I'm interested in what is exactly meant in the documentation since it quotes newline)
You actually would need to transform the \n part into an actual newline. That can be done with the $'' syntax:
python -c $'for i in range(10):\n print i'
0
1
2
3
4
5
6
7
8
9
You can also reach that result with echo -e or printf
$ python -c "$(echo -e "for i in range(10):\n print i")"
You could also use a here string:
$ python <<< $(echo -e "for i in range(10):\n print i")
See section 3.1.2.4 ANSI-C Quoting of the Bash Manpage for more information.
Remove \n
python -c "for i in range(10): print i"
Or
You can use ; for using multiple line in for loop
python -c "for i in range(10): print '1st newline';print '2nd newline';print i"
You can run a multi-line python -c statement by adding CR characters in your line:
python -c "for i in range(10):^M print (i)^M print ('Hello:' + str(i*i))"
where ^M is not actually ^ followed by M, it is actually the character you get when you type [CTRL-v][CTRL-m]. Notice the space after this character, which means there are two print statements in the for loop, and it should print:
0
Hello:0
1
Hello:1
....
9
Hello:81
You can do this in a bash script too:
#!/bin/bash
A="python -c \"for i in range(10):^M print (i)^M print ('Hello:' + str(i*i))\""
eval $A

Why does this snippet with a shebang #!/bin/sh and exec python inside 4 single quotes work?

I'm trying to understand one of the answers to this question:
Cannot pass an argument to python with "#!/usr/bin/env python"
#!/bin/sh
''''exec python -u -- "$0" ${1+"$#"} # '''
This works well, but I do not understand why it works with four ticks at the beginning of that line rather than three.
In addition, why the hash near the end of that string?
Python supports triple-quoted strings:
'''something'''
Shell supports only single-quoted strings:
'something'
By using four quotes, sh sees that as 2 empty strings, but Python sees the first three as the start of a triple-quoted string, and includes the fourth as part of the string value.
The rest of the line is then interpreted as a command by sh, but as part of a string by Python.
The # then forms a comment as far as sh is concerned, but it is still a string to Python, closing it off with a closing triple-quote.
So, to summarize:
sh sees: empty string ('') - empty string ('') - command (exec python -u -- "$0" ${1+"$#"}) - comment (# ''')
Python sees: triple-quoted string literal (containing the characters 'exec python -u -- "$0" ${1+"$#"} #)
So sh executes that command, replacing itself with the python -u -- with the script name and the rest of the command line arguments, and Python reads this file and just sees an initial string literal that isn't going anywhere.
Because it is the first string literal in the file, it'll be set as the docstring for the __main__ module but that is hardly going to matter if this is the main script.
I just use:
#!/bin/sh
''':'
exec python -tt "$0" "$#"
'''
# The above shell shabang trick is more portable than /usr/bin/env and supports adding arguments to the interpreter (python -tt)

Long programs using python -c switch

I would like to use python for things I've been doing using bash. Is it possible to use the -c switch for long programs, e.g. a for loop with two statements? This would let me use python directly from command line, just like bash or php.
Thanks.
EDIT: Don't know how I missed it, simply doing a python -c ' and then pressing enter does what I've wanted to do. I'd tried a lot of variations, and one using a \ but that didn't work, so I asked the question.
e.g.
$python -c '
>print "x"
>for i in range(3):
> print "y" '
does what I wanted to do, though Rod's answer looks good too.
No problem if your underlying shell is bash, since you can continue an argument across multiple lines if an opened ' (quote) is not yet closed -- e.g.:
$ python -c'for x in range(3):
> if x!=1:
> print x'
0
2
$
The > is bash's default PS2, the "multi-line continuation prompt", as distinguished from $, AKA PS1, the normal "start entering a command" prompt.
If you can't use such multi-line continuation, multiple nested block statements (such as an if within a loop) could otherwise be problematic.
You can use compound statements, using the semi-colon to delimiter the statements, such as
python -c "for x in range(0,3) : print x; print x
Then output would then be:
0
0
1
1
2
2
see http://docs.python.org/reference/compound_stmts.html
When used inside a script, I think it would be better to have python read the script from standard input, like so:
#!/bin/bash
python - arg1 arg2 <<END
import sys
print 'Arg:', sys.argv[1:]
END
This uses bash's HEREDOC syntax.
If you are running from a bash script, just use quotes:
#!/bin/sh
python -c 'import os
for i in range(3):
for j in range(3):
print i*j
'
echo "done"
Otherwise, if using the cmd line, use ; semicolons to seperate statements, or use single quotes again to wrap around to the next line:
python -c 'import os
> for i in range(3):
> for j in range(3):
> print i*j
> '

Categories