Can not pass special character to subprocess in python

Viewed 122

I have this command which I can get the external IP address returned from Unix shell, so I can use it in my server:

ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'

on my mac, terminal returns:

192.168.1.3

How do I get this outputted in the python script? I have tried:

import subprocess

command = ['ifconfig', '|', 'sed', '-En', 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p']
p = subprocess.Popen(command, stdout=subprocess.PIPE)
text = p.stdout.read()
retcode = p.wait()

and I got error says

ifconfig: interface | does not exist

Thanks in advance!

2 Answers

Your shell command is invoking two commands, the output of ifconfig is used as input to sed. You could emulate this using subprocess but the sed call is just doing some text manipulation, so a cleaner approach would just be to use Python for that step. For example:

import re
import subprocess

pattern = r'inet (?:addr:)?(?!127\.0\.0\.1)((?:\d+\.){3}\d+)'

p = subprocess.Popen(['ifconfig'], stdout=subprocess.PIPE)
text = re.search(pattern, p.stdout.read()).group(1)
retcode = p.wait()
ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'

you are trying to envoke 2 shell commands, ifconfig and sed, which is okay. but, these are shell commands and the shell keyword argument must be set to true when calling the subprocess.Poen.

use the communictae method, it better to use here. and dispatch the command as a string not list.

import subprocess

command = ' '.join(['ifconfig', '|', 'sed', '-En', 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'])
p = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
stdoutdata, stderrdata = p.communicate()  #this is blocking
for line in stdoutdata:
    #do some thing with line

Popen.communicate(input=None)

Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate. The optional input argument should be a string to be sent to the child process, or None, if no data should be sent to the child.

Related