1

I am writing a script that needs to call an external command using the subprocess module. The problem is I need to pass a file provided by the user to the command so it looks similar to this:

p = subprocess.run("command arg1 arg2 {userfile}".format(userfile=f),
                   capture_output=True, text=True, shell=True)

I have to run the command with shell=True to make it work and that's why I pass the command as string instead of list.

The problem is that someone may pass a file named: somefile && rm -rf ~, which is a vaild file name for a weird reason (at least on windows. don't know about mac/linux), and then bad things happen.

Therefore, I need to escape the user input before passing it to the shell. I wanted to use the built in shlex.quote function for this, so with the above example I get: 'somefile && rm -rf ~' and the command becomes: command arg1 arg2 'somefile && rm -rf ~cmd' which should work on unix systems. The problem is this escaping doesn't work on windows with the command prompt so my script fails on my windows machine.

Is there a built in or third party function/library that can escape command line arguments properly for all platforms, or at least for windows (because shlex.quote works on unix)?

I am developing on windows so I need this script to work on this platform and I don't thing that something like "{userfile}" is good enough.

Any solution for python 3 would be appreciated.

Shai Avr
  • 942
  • 8
  • 19

1 Answers1

1

Pass a list to subprocess eg

p = subprocess.run(["command", "arg1", "arg2" , f],
                   capture_output=True, text=True)

Update regarding windows

Using an absolute path to the expected binary is usually the best approach, changing the command to:

p = subprocess.run(["C:\\path\\to\\binary.exe", "arg1", "arg2" , f],
                   capture_output=True, text=True)

If the absolute path is unknown then which can be used to find your binary (on recent versions of windows, I tested on Windows 7).

>>> p = subprocess.run(["which", "python"], stdout=subprocess.PIPE)
>>> python_binary = p.stdout.strip().decode()  # Convert back to str
>>> python_binary
'C:\\Program Files\\Python36\\python.exe'
Tim
  • 2,510
  • 1
  • 22
  • 26
  • I can't. The command I am using is on my `PATH` so I need to use `shell=True` and according to the [docs](https://docs.python.org/3/library/subprocess.html#subprocess.Popen), it's recommended to pass a string in this case rather than a sequence – Shai Avr Jul 26 '19 at 12:17
  • Use the absolute path eg `subprocess.run(["C:/path/to/binary", "arg1", "arg2" , f])` – Tim Jul 26 '19 at 12:18
  • But I don't want to hardcode the path. It may not be the same across different computers or platforms. – Shai Avr Jul 26 '19 at 12:27
  • One potential solution then is to use the `where` command to find the location of your binary (`where` is available on recent versions of windows) eg `c:\>where ping` returns `C:\Windows\System32\PING.EXE` – Tim Jul 26 '19 at 12:32