python multiline command running from bash - python

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

Related

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")'

call awk from inside python generate error

Ive to run awk from the python. When I run the script from the terminal, gives the desired output but showing error when
executing from inside the python.
runAwk = '''awk '{printf $1}{for(i=2;i<=NF;i++)printf "|"$i}{printf "\n"}' final.txt'''
os.system(runAwk)
gives the error:
awk: line 1: runaway string constant " ...
when I surfed from the web, I found that awk can not be used with os module and there are not much contents. I am confused how to proceed ahead.
The \n in your runAwk string is being interpreted by Python as a literal newline character, rather than being passed through to awk as the two characters \ and n. If you use a raw string instead, by preceding the opening triple-quotes with an r:
runAwk = r'''awk '{printf $1}{for(i=2;i<=NF;i++)printf "|"$i}{printf "\n"}' final.txt'''
... then Python won't treat \n as meaning "newline", and awk will see the string you intended.

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)

How to run multiple lines using python -c

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.

symbols in command line argument.. python, bash

I am writing a python script on Linux for twitter post using API, Is it possible to pass symbols like "(" ")" etc in clear text without apostrophes....
% ./twitterupdate this is me #works fine
% ./twitterupdate this is bad :(( #this leaves a error on bash.
Is the only alternative is to enclose the text into --> "" ?? like..
% ./twitterupdate "this is bad :((" #this will reduce the ease of use for the script
Is there any workaround?
Yes, quoting the string is the only way. Bash has its syntax and and some characters have special meaning. Btw, using "" is not enough, use apostrophes instead. Some characters will still get interpretted with normal quotation marks:
$ echo "lots of $$"
lots of 15570
$ echo 'lots of $$'
lots of $$
http://www.gnu.org/software/bash/manual/bashref.html#Quoting

Categories