In Python, we can use subprocess
to run bat
(cmd
) scripts natively, like
import subprocess as sp
sp.run(["D:/Temp/hello.bat"])
works fine. However, it cannot run ps1
scripts natively, codes like
import subprocess as sp
sp.run(["D:/Temp/hello.ps1"])
will cause `WinError 193: %1 is not a valid Win32 application" to be raised.
I thought it was because the .PS1
had not been showing up in PATHEXT
and added it there, but it failed again. I also tried the way provided in this answer which seems to be setting the file execution policy and it still wouldn't work.
I know it would work when I add the executor in the call like sp.run(["pwsh", "D:/Temp/hello.ps1"])
or use a bat
script as intermediate, but that's not desired. I am using a program which is badly ported from *nix to Windows and it just call whatever I provided as a single executable (on *nix we can use shebang, but not on windows).
Batch files are special in that they are the only type of interpreter-based script files directly recognized as executables by the system.
For any other script files, including PowerShell scripts (*.ps1
), the only way you can achieve their execution when directly invoked is:
Redefine the command that is used by the Windows shell to open *.ps1
files (i.e. the command associated with the Open
shell verb) so as to pass the file path at hand to the PowerShell CLI (powershell.exe
for Windows PowerShell, pwsh
for PowerShell (Core) 7) for execution.
Note: The default behavior when opening *.ps1
files from outside PowerShell (which includes invoking them from cmd.exe
and double-clicking in File Explorer) is to open them for editing. Changing the behavior to executing PowerShell scripts may be unexpected by other users, so it's best to limit this change to your own user account..
The SuperUser answer you link to shows how do to that, via the registry; note that the linked code requires elevation, i.e. running with administrative privileges.
In order for subprocess.run()
to use the redefined command, shell=True
must be passed (which calls via cmd.exe
, which automatically invokes the default Windows shell operation on non-executable files):
import subprocess as sp
# Performs the "Open" Window shell operation on the *.ps1 file.
sp.run(["D:/Temp/hello.ps1"], shell=True)
If you don't want to redefine the command associated with the Open
shell verb, your only option is indeed to call *.ps1
files via the PowerShell CLI; e.g.:
import subprocess as sp
sp.run(["powershell.exe", "-NoProfile", "-File", "D:/Temp/hello.ps1"])