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.

1
  • 1
    You use list, instead of string. subprocess.run(["ls", "-l"]) Commented Jul 26, 2019 at 12:13

1 Answer 1

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'
Sign up to request clarification or add additional context in comments.

4 Comments

I can't. The command I am using is on my PATH so I need to use shell=True and according to the docs, it's recommended to pass a string in this case rather than a sequence
Use the absolute path eg subprocess.run(["C:/path/to/binary", "arg1", "arg2" , f])
But I don't want to hardcode the path. It may not be the same across different computers or platforms.
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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.