Repository navigation
Windows: Provide an option to disable search in CWD on shutil.which #91558
Description
Activity
WinAPI
CreateProcessW()and the CMD shell respectNeedCurrentDirectoryForExePathW(). This function is false only if theExeNameargument contains no backslashes and the environment variableNoDefaultCurrentDirectoryInExePathis defined. The API should be used instead of the environment variable, in case the function is modified to depend on a different variable, registry setting, or a compatibility mode.Note that
NeedCurrentDirectoryForExePathW()does not handle forward slash as a path separator, as is actually the case with many functions that work with file paths in Windows. TheExeNameargument has to be normalized to replace forward slashes with backslashes if implementing a Windows-style search matters. In the Windows API, if a filename has no root or drive, the system's search routine tries to resolve it against each directory in the search path, until the first match, even if the filename contains one or more backslashes. In this case, the current directory is the second directory that's checked, after the application directory. In POSIX, as far as I know, a relative path that contains one or more slashes is always resolved against the current directory.Reacted by Eliah KaganAre you suggesting that
NeedCurrentDirectoryForExePathWshould be used rather than providing a parameter to override this behaviour?Yes, a long time ago I suggested that
shutil.which()should checkNeedCurrentDirectoryForExePathW()(added in Windows Vista, circa 2006) to determine whether or not the current working directory should be included. This would match the behavior of bothCreateProcessW()and the CMD shell, as used bysubprocess.Popenwith bothshell=False(default) andshell=True. However, nothing came of the suggestion.Since
shutil.which()implements a POSIX style search, the check only applies when thecmdargument contains no slashes or backslashes, as determined byos.path.dirname().Reacted by Naveen M KThanks for the reply! Using that API makes sense as it matches with how cmd/CreateProcess works, though I think it would be better to have a flag in
shutil.whichwhich disables this behaviour. What do you think?One difference between current
shutil.which()andCreateProcessW(), is that the later always prefers binaries in the system directory, so things liketar,curl,bashwould then never return the version in PATH, but the Windows shipped version. I depend on this in various scripts, though not sure how common that is..CreateProcessW()... always prefers binaries in the system directoryCreateProcessW()first checks the application directory, even if the path is a relative path that has one or more backslashes (e.g.r'bin\bash.exe'). Next, ifNeedCurrentDirectoryForExePathW()is true, it checks the current directory. Next it checks the "System32" directory and the "Windows" directory. If the command still isn't found, it checks thePATHdirectories, which can explicitly include the current directory as ".". When checking a directory, it looks for the given name and also the name with ".EXE" appended. It doesn't usePATHEXT.shutil.which()is closer to the CMD shell's executable file search, since it usesPATHEXTand doesn't prefer the application directory and system directories overPATH. Starting with Windows Vista, CMD also callsNeedCurrentDirectoryForExePathW()to determine whether to implicitly include the current directory in the search path.That said,
shutil.which()usesPATHEXTdifferently from the shell, and thus differently fromos.system()andsubprocess.Popenwithshell=True. CMD usesPATHEXTto expand the search with a set of extensions to try appending in addition to checking for the given name. It doesn't use it as a security policy that limits the search to just an allowed set of file types.Reacted by Christoph Reiter, Naveen M K and Eliah Kagan- added 2 commits that reference this issue
on Nov 25, 2023
I would like to search for an executable in the PATH but not from the current working directory (
cwd). By default, as documented inshutil.which, it prepends the cwd before PATH, so it's not possible to get the expected executable.For example, I have a
whoami.exein the cwd but I would like to get the one from the system32 directory; there's no way to do so usingshutil.which.I think it makes sense for that to be the default behaviour since that's how it is on Windows. I would like to have a parameter to disable this behaviour in
shutil.which.Thanks!