92

I'm currently studying penetration testing and Python programming. I just want to know how I would go about executing a Linux command in Python. The commands I want to execute are:

echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080

If I just use print in Python and run it in the terminal will it do the same as executing it as if you was typing it yourself and pressing Enter?

Xanmashi
  • 1,053

6 Answers6

147

You can use os.system(), like this:

import os
os.system('ls')

Or in your case:

os.system('echo 1 > /proc/sys/net/ipv4/ip_forward')
os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080')

Better yet, you can use subprocess's call, it is safer, more powerful and likely faster:

from subprocess import call
call('echo "I like potatos"', shell=True)

Or, without invoking shell:

call(['echo', 'I like potatos'])

If you want to capture the output, one way of doing it is like this:

import subprocess
cmd = ['echo', 'I like potatos']
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

o, e = proc.communicate()

print('Output: ' + o.decode('ascii'))
print('Error: '  + e.decode('ascii'))
print('code: ' + str(proc.returncode))

I highly recommend setting a timeout in communicate, and also to capture the exceptions you can get when calling it. This is a very error-prone code, so you should expect errors to happen and handle them accordingly.

https://docs.python.org/3/library/subprocess.html

Kira
  • 4,807
  • 33
    os.system is deprecated since version 2.6. Subprocess is the right module to use. – Michelle Welcks Jan 12 '16 at 17:04
  • @binarysubstrate, deprecated as in not supported or not available? I've been recently working on machine with 2.7 (not by choice), and os.system still works. – openwonk Dec 07 '18 at 01:04
  • 1
    Also, if using subprocess.call as recommended, you may need to specify shell=True... see here – openwonk Dec 07 '18 at 01:12
  • With Python 3.4 the shell=True has to be stated otherwise the call command will not work. By default call will try to open a file specified by the string unless the shell=True is set. It also looks like that in Python 3.5 call is replaced with run – DLH Feb 20 '19 at 20:19
  • Generic POSIX code should probably call decode() with charset from LC_CTYPE environment variable. – Mikko Rantalainen Feb 28 '19 at 19:58
33

The first command simply writes to a file. You wouldn't execute that as a shell command because python can read and write to files without the help of a shell:

with open('/proc/sys/net/ipv4/ip_forward', 'w') as f:
    f.write("1")

The iptables command is something you may want to execute externally. The best way to do this is to use the subprocess module.

import subprocess
subprocess.check_call(['iptables', '-t', 'nat', '-A',
                       'PREROUTING', '-p', 'tcp', 
                       '--destination-port', '80',
                       '-j', 'REDIRECT', '--to-port', '8080'])

Note that this method also does not use a shell, which is unnecessary overhead.

jordanm
  • 42,678
14

The quickest way:

import os
os.system("your command here")

This isn't the most flexible approach; if you need any more control over your process than "run it once, to completion, and block until it exits", then you should use the subprocess module instead.

Tom Hunt
  • 10,056
9

As a general rule, you'd better use python bindings whenever possible (better Exception catching, among other advantages.)

For the echo command, it's obviously better to use python to write in the file as suggested in @jordanm's answer.

For the iptables command, maybe python-iptables (PyPi page, GitHub page with description and doc) would provide what you need (I didn't check your specific command).

This would make you depend on an external lib, so you have to weight the benefits. Using subprocess works, but if you want to use the output, you'll have to parse it yourself, and deal with output changes in future iptables versions.

Jérôme
  • 1,938
  • Another thing to note is that unit testing code with subprocess calls is less useful. Yes, you can mock out the calls, but the tests have to make assumptions about the results on the external calls. – jordanm Oct 23 '15 at 17:43
  • Your answer is more like advice. The OP has specifically asked how he can run a linux system command from python. – kapad Apr 05 '18 at 09:03
  • @kapad Yes, but he also specified why he wanted to do so and I proposed an alternative. – Jérôme Apr 05 '18 at 09:23
3

A python version of your shell. Be careful, I haven't tested it.

from subprocess import run

def bash(command):
        run(command.split())

>>> bash('find / -name null')
/dev/null
/sys/fs/selinux/null
/sys/devices/virtual/mem/null
/sys/class/mem/null
/usr/lib/kbd/consoletrans/null
  • 1
    String splitting only works for the simple cases. For shell-style command splitting use shlex.split – iruvar Sep 20 '20 at 15:00
1

If you want to execute this command in some other system after SSH then you might have to use the module named Paramiko. It is really useful.

If the command execution has to be done from local machine then os module functions will be helpful.

Homepage: https://www.paramiko.org/

Development: https://github.com/paramiko/paramiko

JayRizzo
  • 105