1

I've been wrestling with solutions from "How do I use sudo to redirect output to a location I don't have permission to write to?" and "append line to /etc/hosts file with shell script" with no luck.

I want to "append 10.10.10.10 puppetmaster" at the end of /etc/hosts. (Oracle/Red-Hat linux).

Been trying variations of:

subprocess.call("sudo -s", shell=True)

subprocess.call('sudo sh -c" "10.10.10.10 puppetmaster" >> /etc/hosts"', shell=True)

subprocess.call(" sed -i '10.10.10.10 puppetmaster' /etc/hosts", shell=True)

But /etc/hosts file stands still. Can someone please point out what I'm doing wrong?

Community
  • 1
  • 1

4 Answers4

2

Simply use dd:

subprocess.Popen(['sudo', 'dd', 'if=/dev/stdin',
    'of=/etc/hosts', 'conv=notrunc', 'oflag=append'],
    stdin=subprocess.PIPE).communicate("10.10.10.10 puppetmaster\n")
Daniel
  • 42,087
  • 4
  • 55
  • 81
1

The problem you are facing lies within the scope of the sudo.

The code you are trying calls sudo with the arguments sh and -c" "10.10.10.10 puppetmaster". The redirection of the >> operator, however, is done by the surrounding shell, of course with its permissions.

To achieve the effect you want, try starting a shell using sudo which then is given the command:

sudo bash -c 'sh -c" "10.10.10.10 puppetmaster" >> /etc/hosts"'

This will do the trick because the bash you started with sudo has superuser permissions and thus will not fail when it tries to perform the output redirection with >>.

To do this from within Python, use this:

subprocess.call("""sudo bash -c 'sh -c" "10.10.10.10 puppetmaster" >> /etc/hosts"'""", shell=True)

But of course, if you run your Python script with superuser permissions (start it with sudo) already, all this isn't necessary and the original code will work (without the additional sudo in the call):

subprocess.call('sh -c" "10.10.10.10 puppetmaster" >> /etc/hosts"', shell=True)
Alfe
  • 56,346
  • 20
  • 107
  • 159
  • 1
    This seems... rather unnecessarily complex and error-prone, compared to a `shell=False` version with only a single shell run as a subprocess to `sudo`. – Charles Duffy Jun 08 '15 at 21:42
  • You are right. I mostly explained the `sudo` with `>>` effect and how to deal with it in general. The concrete situation cries for simpler solutions. – Alfe Jun 08 '15 at 21:47
1

You can do it in python quite easily once you run the script with sudo:

with open("/etc/hosts","a") as f:
    f.write('10.10.10.10 puppetmaster\n')

opening with a will append.

Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
0

If you weren't escalating privileges for the entire script, I'd recommend the following:

p = subprocess.Popen(['sudo', 'tee', '-a', '/etc/hosts'],
                     stdin=subprocess.PIPE, stdout=subprocess.DEVNULL)
p.stdin.write(b'10.10.10.10 puppetmaster\n')
p.stdin.close()
p.wait()

Then you can write arbitrary content to the process's stdin (p.stdin).

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • "sudo ./charles.py" leads to {Traceback (most recent call last): File "./charles.py", line 8, in p.stdin.write('10.10.10.10 puppetmaster\n') TypeError: a bytes-like object is required, not 'str'} am I doing something wrong? –  Jun 08 '15 at 21:55
  • @SndLt: `subprocess` uses bytes by default. Use bytes literals: `b'10...'`. btw, you could use `p.communicate(b'10.10.10.10 puppetmaster\n')` instead of the last 3 lines in the code example. Also, use `stdout=DEVNULL` (it seems you are on Python 3) and you don't need `shell=False` (it is default). – jfs Jun 09 '15 at 14:10
  • @SndLt, ...what J.F. Sebastian said. The only thing you did wrong was not clearly specifying in your question that this was Python 3 rather than Python 2. – Charles Duffy Jun 09 '15 at 15:10
  • @Charles Duffy , I see. So if I want to run this stdin in python3, just set stdout to null? or use p.communicate instead of last 3 lines? Thanks. –  Jun 09 '15 at 16:45
  • @SndLt, well, the last three lines will work otherwise as-is if you make the string a bytestring rather than a Unicode string, _or_ you can use `communicate()`. (Might need a bytestring there too; I'd need to check the docs -- like most of the Real World writing commercial software, I'm still on Python 2). – Charles Duffy Jun 09 '15 at 16:47
  • @SndLt, ...the `DEVNULL` constant is an efficiency, portability and terseness improvement, not a correctness one (on UNIX). – Charles Duffy Jun 09 '15 at 16:48
  • p.communicate works!! I also edited the title of post to pytrhon3. In the future, I'll keep that version in mind. Thanks. –  Jun 09 '15 at 17:01