I need to be able to open a document using its default application in Windows and Mac OS. Basically, I want to do the same thing that happens when you double click on the document icon in Explorer or Finder. What is the best way to do this in Python?
In Mac OS, you can use the “open” command. There is a Windows API call that does something similar, but I don’t remember it offhand.
Okay, the “start” command will do it, so this should work.
Much later update by Edward: os.system works, but it only works with filenames that don’t have any spaces in folders and files in the filename (e.g. A:\abc\def\a.txt).
Okay, clearly this silly-ass controversy continues, so let’s just look at doing this with subprocess.
start are command interpreter things for Mac OS/X and Windows respectively. Now, let’s say we use subprocess. Canonically, you’d use:
try: retcode = subprocess.call("open " + filename, shell=True) if retcode < 0: print >>sys.stderr, "Child was terminated by signal", -retcode else: print >>sys.stderr, "Child returned", retcode except OSError, e: print >>sys.stderr, "Execution failed:", e
Now, what are the advantages of this? In theory, this is more secure — but in fact we’re needing to execute a command line one way or the other; in either environment, we need the environment and services to interpet, get paths, and so forth. In neither case are we executing arbitrary text, so it doesn’t have an inherent “but you can type
'filename ; rm -rf /'” problem, and IF the file name can be corrupted, using
subprocess.call gives us no protection.
It doesn’t actually give us any more error detection, we’re still depending on the
retcode in either case. We don’t need to wait for the child process, since we’re by problem statement starting a separate process.
subprocess is preferred.” However,
os.system() is not deprecated, and it’s the simplest tool for this particular job.
os.system() is the simplest, most straightforward way to do this, and is therefore a correct answer.
subprocess module available on Python 2.4+, not
os.system(), so you don’t have to deal with shell escaping.
import subprocess, os if sys.platform.startswith('darwin'): subprocess.call(('open', filepath)) elif os.name == 'nt': os.startfile(filepath) elif os.name == 'posix': subprocess.call(('xdg-open', filepath))
The double parentheses are because
subprocess.call() wants a sequence as its first argument, so we’re using a tuple here. On Linux systems with Gnome there is also a
gnome-open command that does the same thing, but
xdg-open is the Free Desktop Foundation standard and works across Linux desktop environments.
Just for completeness (it wasn’t in the question), xdg-open will do the same on Linux.
Note that this module supports filenames that have spaces in their folders and files e.g.
A:\abc\folder with spaces\file with-spaces.txt
(python docs) ‘open’ does not have to be added (it is the default). The docs specifically mention that this is like double-clicking on a file’s icon in Windows Explorer.
This solution is windows only.
import os import subprocess def click_on_file(filename): try: os.startfile(filename): except AttributeError: subprocess.call(['open', filename])
If you have to use an heuristic method, you may consider
It’s standard library and despite of its name it would also try to open files:
Note that on some platforms, trying to open a filename using this
function, may work and start the operating system’s associated
program. However, this is neither supported nor portable.
I tried this code and it worked fine in Windows 7 and Ubuntu Natty:
import webbrowser webbrowser.open("path_to_file")
This code also works fine in Windows XP Professional, using Internet Explorer 8.
Start does not support long path names and white spaces. You have to convert it to 8.3 compatible paths.
import subprocess import win32api filename = "C:\Documents and Settings\user\Desktop\file.avi" filename_short = win32api.GetShortPathName(filename) subprocess.Popen('start ' + filename_short, shell=True )
The file has to exist in order to work with the API call.
If you want to go the
subprocess.call() way, it should look like this on Windows:
import subprocess subprocess.call(('cmd', '/C', 'start', '', FILE_NAME))
You can’t just use:
start is not an executable but a command of the
cmd.exe program. This works:
subprocess.call(('cmd', '/C', 'start', FILE_NAME))
but only if there are no spaces in the FILE_NAME.
enquotes the parameters properly, the
start command has a rather strange syntax, where:
does something else than:
The first quoted string should set the title of the window. To make it work with spaces, we have to do:
start "" "my notes.txt"
which is what the code on top does.
I am pretty late to the lot, but here is a solution using the windows api. This always opens the associated application.
import ctypes shell32 = ctypes.windll.shell32 file = 'somedocument.doc' shell32.ShellExecuteA(0,"open",file,0,0,5)
A lot of magic constants. The first zero is the hwnd of the current program. Can be zero. The other two zeros are optional parameters (parameters and directory). 5 == SW_SHOW, it specifies how to execute the app.
ShellExecute API docs for more info.
os.startfile(path, ‘open’) under windows is good because when spaces exist in the directory, os.system(‘start’, path_name) can’t open the app correct and when the i18n exist in the directory, os.system needs to change the unicode to the codec of the console in Windows.
on mac os you can call ‘open’
import os os.popen("open myfile.txt")
this would open the file with TextEdit, or whatever app is set as default for this filetype
If you want to specify the app to open the file with on Mac OS X, use this:
os.system("open -a [app name] [file name]")
On windows 8.1, below have worked while other given ways with
subprocess.call fails with path has spaces in it.
subprocess.call('cmd /c start "" "any file path with spaces"')
By utilizing this and other’s answers before, here’s an inline code which works on multiple platforms.
import sys, os, subprocess subprocess.call(('cmd /c start "" "'+ filepath +'"') if os.name is 'nt' else ('open' if sys.platform.startswith('darwin') else 'xdg-open', filepath))