Search code examples
pythonpathsubprocessjupyter

python: subprocess.Popen, openvpn command not found


OS X 10.13.6 Python 3.6

I am trying to run the following command from a jupyter notebook:

vpn_cmd = '''
sudo openvpn 
--config ~/Downloads/configs/ipvanish-US-Chicago-chi-a49.ovpn 
--ca ~/Downloads/configs/ca.ipvanish.com.crt'''

proc = Popen(vpn_cmd.split(), stdout=PIPE, stderr=STDOUT)
stdout, stderr = proc.communicate()
print(stdout.decode())

But get the error:

sudo: openvpn: command not found

What I've tried:

  • added export PATH="/usr/local/sbin:$PATH" to my ~/.bash_profile and can run the the sudo openvpn command from my terminal
  • edited my sudoers file so sudo no longer prompts for a password
  • called sudo which openvpn and tried adding /usr/local/sbin/openvpn to my sys.path within python
  • not splitting vpn_cmd and setting shell=True
  • tried packaging it in a test.py script and executing from the terminal, but it just hangs at the proc.communicate() line
  • specified the full path for the --config and --ca flags

So far, nothing has fixed this. I can run openvpn from my terminal just fine. It seems like a simple path issue but I can't figure out what I need to add to my python path. Is there something particular with the jupyter notebook kernel?


Solution

  • Jupyter probably isn't picking up your personal .bashrc settings, depending also on how you are running it. Just hardcode the path or augment the PATH in your Python script instead.

    With shell=False you don't get the tildes expanded; so you should change those to os.environ["HOME"], or make sure you know in which directory you run this, and use relative paths.

    You should not be using Popen() if run can do what you require.

    home = os.environ["HOME"]
    configs = home + '/Downloads/configs/'
    r = subprocess.run(
        ['sudo', '/usr/local/sbin/openvpn', 
         '--config', configs + 'ipvanish-US-Chicago-chi-a49.ovpn', 
         '--ca', configs + 'ca.ipvanish.com.crt'],
        stdout=PIPE, stderr=PIPE, text=True)
    print(r.stdout)
    

    Tangentially, you should not use "...".split() to split a command line. In this case, it would not matter, but for robustness, probably still prefer shlex.split(), which knows how to handle backslashes and quoted strings in shell commands properly.

    This answer originally used universal_newlines=True but this was changed to the more understandable text=True in Python 3.7. The old syntax will still work, but I updated this answer to reflect current best practice.