I had script on bash where I generated username, password, ssh-key for user.
Part for creating of ssh-key:
su $user -c "ssh-keygen -f /home/$user/.ssh/id_rsa -t rsa -b 4096 -N ''"
How can I do the same in Python with os.system? I tried this:
os.system('su %s -c "ssh-keygen -f /home/%s/.ssh/id_rsa -t rsa -b 4096 -N ''"', user)
TypeError: system() takes at most 1 argument (2 given)
Also I tried:
os.system('su user -c "ssh-keygen -f /home/user/.ssh/id_rsa -t rsa -b 4096 -N ''"')
Of course, it doesn't work either.
Format your instructions with the os package; for instance:
import os
user = 'joe'
ssh_dir = "/home/{}/.ssh/id_rsa".format(user)
os.system("ssh-keygen -f {} -t rsa -b 4096 -N ''".format(ssh_dir))
os.system is very close to a bash command line because it uses an underlying shell (like its cousins subprocess.call... using shell=True)
In your case, there's little interest using subprocess since your command runs a command, so you cannot really use argument protection by subprocess fully.
Pass the exact command, but the only change would be to protect the simple quotes, else python sees that as string end+string start (your string is protected by simple quotes already) and they're eliminated.
Check this simpler example:
>>> 'hello '' world'
'hello world'
>>> 'hello \'\' world'
"hello '' world"
that's a kind of worst-case when you cannot use either double or simple quotes to protect the string because you're using the other flavour within. In that case, escape the quotes using \:
os.system('su $user -c "ssh-keygen -f /home/$user/.ssh/id_rsa -t rsa -b 4096 -N \'\'"')
Use the subprocess module:
import subprocess
username = 'user'
result, err = subprocess.Popen(
'su %s -c "ssh-keygen -f /home/%s/.ssh/id_rsa -t rsa -b 4096 -N ''"' % (username, username),
stdout=subprocess.PIPE,
shell=True
).communicate()
if err:
print('Something went wrong')
else:
print(result)
Edit: this is the 'fast' way to do that, you should't use shell=True if you can't control the input since it allows code execution as said here
Related
I made a python3 script and i need to run a bash command to make it work. i have tried os.system and subprocess but neither of them fully work to run the whole command, but when i run the command by itself in the terminal then it works perfect. what am i doing wrong?
os.system("fswebcam -r 640x480 --jpeg 85 -D 1 picture.jpg &> /dev/null")
os.system("echo -e "From: abc#gmail.com\nTo: abc1#gmail.com\nSubject: package for ryan\n\n"package for ryan|uuenview -a -bo picture.jpg|sendmail -t")
or
subprocess.run("fswebcam -r 640x480 --jpeg 85 -D 1 picture.jpg &> /dev/null")
subprocess.run("echo -e "From: abc#gmail.com\nTo: abc1#gmail.com\nSubject: package for ryan\n\n"package for ryan|uuenview -a -bo picture.jpg|sendmail -t")
This is supposed to take a picture and email it to me. With os.command it gives an error "the recipient has not been specified "(even though it works perfect in terminal by itself) and with subprocess it doesnt run anything
Best Practice: Completely Replacing the Shell with Python
The best approach is to not use a shell at all.
subprocess.run([
'fswebcam',
'-r', '640x480',
'--jpeg', '85',
'-D', '1',
'picture.jpg'],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
Doing this with a pipeline is more complicated; see https://docs.python.org/3/library/subprocess.html#replacing-shell-pipeline, and many duplicates already on this site.
Second Choice: Using sh-compatible syntax
echo is poorly defined by the POSIX sh standard (the standard document itself advises against using it, and also fully disallows -e), so the reliable thing to do is to use printf instead.
Passing the text to be sent as a literal command-line argument ($1) gets us out of the business of figuring out how to escape it for the shell. (The preceding '_' is to fill in $0).
subprocess.run("fswebcam -r 640x480 --jpeg 85 -D 1 picture.jpg >/dev/null 2>&1",
shell=True)
string_to_send = '''From: abc#gmail.com
To: abc1#gmail.com
Subject: package for ryan
package for ryan
'''
p = subprocess.run(
[r'''printf '%s\n' "$1" | uuenview -a -bo picture.jpg | sendmail -t''',
"_", string_to_send],
shell=True)
I have written a python script which includes this line:
response = subprocess.check_output(['/usr/bin/sudo /bin/su - backup -c "/usr/bin/ssh -q -o StrictHostKeyChecking=no %s bash -s" <<\'EOF\'\nPATH=/usr/local/bin:$PATH\nmvn --version|grep -i Apache|awk \'{print $3}\'|tr -d \'\n\'\nEOF' % i], shell=True)
This is in a for loop that goes through a list of hostnames and each one I want to check the result of the command on it. This works fine when I run it myself, however, this script is to be run by a system user (shinken - a nagios fork) and at that point I hit an issue.
shinken ALL=(ALL) NOPASSWD: ALL
However, I wanted to restrict the user to only allow it to run as the backup user:
shinken ALL=(backup) NOPASSWD: ALL
But when I run the script I get:
sudo: no tty present and no askpass program specified
I have read around this and tried a few things to fix it. I tried adding -t to my ssh command, but that didn't help. I believe I should be able to run the command with something similar to:
response = subprocess.check_output(['/usr/bin/sudo -u backup """ "/usr/bin/ssh -q -o StrictHostKeyChecking=no %s bash -s" <<\'EOF\'\nPATH=/usr/local/bin:$PATH\njava -version|grep -i version|awk \'{print $3}\'|tr -d \'\n\'\nEOF""" ' % i], shell=True)
But then I get this response:
subprocess.CalledProcessError: Command '['/usr/bin/sudo -u backup """ "/usr/bin/ssh -q -o StrictHostKeyChecking=no bamboo-agent-01 bash -s" <<\'EOF\'\nPATH=/usr/local/bin:$PATH\njava -version|grep -i version|awk \'{print $3}\'|tr -d \'\n\'\nEOF""" ']' returned non-zero exit status 1
If I run the command manually I get:
sudo: /usr/bin/ssh: command not found
Which is strange because that's where it lives.... I've no idea if what I'm trying is even possible. Thanks for any suggestions!
As for sudo:
shinken ALL=(backup) NOPASSWD: ALL
...only works when you switch directly from shinken to backup. You aren't doing that here. sudo su - backup is telling sudo to switch to root, and to run the command su - backup as root. Obviously, then, if you're going to use sudo su (which I've advised against elsewhere), you need your /etc/sudoers configuration to support that.
Because your /etc/sudoers isn't allowing direct the switch to root you're requesting, it's trying to prompt for a password, which requires a TTY, which is thus causing a failure.
Below, I'm rewriting the script to switch directly from shinken to backup, without going through root and running su:
As for the script:
import subprocess
remote_script='''
PATH=/usr/local/bin:$PATH
mvn --version 2>&1 | awk '/Apache/ { print $3 }'
'''
def maven_version_for_host(hostname):
# storing the command lets us pass it when constructing a CalledProcessError later
# could move it directly into the Popen creation if you don't need that.
cmd = [
'sudo', '-u', 'backup', '-i', '--',
'ssh', '-q', '-o', 'StrictHostKeyChecking=no', str(hostname),
'bash -s' # arguments in remote-command position to ssh all get concatenated
# together, so passing them as one command aids clarity.
]
proc = subprocess.Popen(cmd,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
response, error_string = proc.communicate(remote_script)
if proc.returncode != 0:
raise subprocess.CalledProcessError(proc.returncode, cmd, error_string)
return response.split('\n', 1)[0]
When I run this at command line it generates my Jasper report correctly:
jasperstarter pr "C:\users\ant\jaspersoftworkspace\myreports\carereport.jrxml" -f pdf -t postgres -H localhost -n template_postgis_20 -u postgres -p postgres -P SiteID=123
However, if I then try to run it through python with the following code the report doesn't get created. Am I messing up the syntax somewhere?
import subprocess
from subprocess import call
subprocess.call(["cmd","/C","jasperstarter","pr","""C:\users\ant\jaspersoftworkspace\myreports\carereport.jrxml""","-f","pdf",
"-t","postgres","-H","localhost","-n","template_postgis_20","-u","postgres","-p","postgres",
"-P","SiteID=123"], shell=True)
EDIT:
Following the comments, I tried running this at cmd after typing python to bring up >>>:
jasperstarter pr "C:\users\ant\jaspersoftworkspace\myreports\carereport.jrxml" -f pdf -t postgres -H localhost -n template_postgis_20 -u postgres -p postgres -P SiteID=123
This time I got a syntax error at -u. I then tried reordering the parameters and the syntax error then occurred at the same character number, rather than at the -u. So is there a maximum line length when entering commands in python at cmd?
\a is a escape sequence that is same to \x07 (BEL). You should escape \ or use raw string literal to make \a represent \a literally.
>>> '\a' # not escaped
'\x07'
>>> '\\a' # escaped
'\\a'
>>> r'\a' # raw string literal
'\\a'
So, replace following:
"""C:\users\ant\jaspersoftworkspace\myreports\carereport.jrxml"""
with
"""C:\\users\\ant\\jaspersoftworkspace\\myreports\\carereport.jrxml"""
or
r"""C:\users\ant\jaspersoftworkspace\myreports\carereport.jrxml"""
UPDATE
Try following:
subprocess.call(r'jasperstarter pr "C:\users\ant\jaspersoftworkspace\myreports\carereport.jrxml" -f pdf -t postgres -H localhost -n template_postgis_20 -u postgres -p postgres -P SiteID=123', shell=True)
I'm trying to execute a rsync command via subrocess & popen. Everything's ok until I don't put the rsh subcommand where things go wrong.
from subprocess import Popen
args = ['-avz', '--rsh="ssh -C -p 22 -i /home/bond/.ssh/test"', 'bond#localhost:/home/bond/Bureau', '/home/bond/data/user/bond/backups/']
p = Popen(['rsync'] + args, shell=False)
print p.wait()
#just printing generated command:
print ' '.join(['rsync']+args)
I've tried to escape the '--rsh="ssh -C -p 22 -i /home/bond/.ssh/test"' in many ways, but it seems that it's not the problem.
I'm getting the error
rsync: Failed to exec ssh -C -p 22 -i /home/bond/.ssh/test: No such file or directory (2)
If I copy/paste the same args that I output at the time, I'm getting a correct execution of the command.
Thanks.
What happens if you use '--rsh=ssh -C -p 22 -i /home/bond/.ssh/test' instead (I removed the double quotes).
I suspect that this should work. What happens when you cut/paste your line into the commandline is that your shell sees the double quotes and removes them but uses them to prevent -C -p etc. from being interpreted as separate arguments. when you call subprocess.Popen with a list, you've already partitioned the arguments without the help of the shell, so you no longer need the quotes to preserve where the arguments should be split.
Having the same problem, I googled this issue extensively. It would seem you simply cannot pass arguments to ssh with subprocess. Ultimately, I wrote a shell script to run the rsync command, which I could pass arguments to via subprocess.call(['rsyncscript', src, dest, sshkey]). The shell script was: /usr/bin/rsync -az -e "ssh -i $3" $1 $2
This fixed the problem.
Expected Behavior
Under normal circumstances, I can issue tshark -E separator='#' under linuxSee Note A and force it to display fields separated by #, as shown below...
[mpenning#hotcoffee ~]$ tshark -r scp_test.pcap -e frame.number -e ip.src_host -e tcp.srcport -E separator='#' -T fields tcp | less
1#192.168.12.236#33088
2#192.168.12.238#22
3#192.168.12.236#33088
...
Unexpected Behavior
Likewise, I thought I would run the same command through subprocess.Popen(), columnify, and colorize based on some analysis... all my analysis depends on the output being separated by # when I run the script... however, my script is not using #... instead, it uses a single-quote; I am not sure I understand why this is happening.
Script
import subprocess
import sys
filename = sys.argv[1].strip()
fields = ['frame_num', 'IP Src', 'TCP Src']
sep = '#'
cmd = r"""tshark -r %s -e frame.number -e ip.src_host -e tcp.srcport -E separator='%s' -T fields tcp""" % (filename, sep)
subcmd = cmd.split(' ')
lines = subprocess.Popen(subcmd, stdout = subprocess.PIPE)
for line in lines.communicate()[0].split('\n'):
print line
Results
[mpenning#hotcoffee ~]$ python analyze.py scp_test.pcap | less
1'192.168.12.236'33088
2'192.168.12.238'22
3'192.168.12.236'33088
4'192.168.12.238'22
5'192.168.12.236'33088
6'192.168.12.236'33088
7'192.168.12.238'22
8'192.168.12.236'33088
It seemingly does not matter whether I assign sep using any of the following...
sep = '#'
sep = '\#'
sep = re.escape('#') # Desperation attempt ;-)
Question
Can someone explain:
Why my output is not separated with # in the script above.
How I can fix the script using subprocessSee Note B?
End-Notes
Note A. System information:
[mpenning#hotcoffee ~]$ python -V
Python 2.6.6
[mpenning#hotcoffee ~]$ uname -a
Linux hotcoffee 2.6.32-5-amd64 #1 SMP Mon Mar 7 21:35:22 UTC 2011 x86_64 GNU/Linux
[mpenning#hotcoffee ~]$
Note B. Answers using os.system() or os.popen() are not what I'm looking for
tshark is taking the ' from '%s'. don't use the single-quotes:
cmd = r"tshark -r %s -e frame.number -e ip.src_host -e tcp.srcport -E separator=%s -T fields tcp" % (filename, sep)
when you ran it from the command line, Bash stripped the single-quotes off and tshark didn't see them.