PowerShell - Strip .exe from program call

Viewed 92

I really like the new PowerShell (7.2.4, in my case). However, I have to run certain programs that require a specific syntax:

$$ <prog> <ending>

<prog> is the name of the program, e.g. test, while the argument could be anything, e.g. this. The tricky part is that the program internally searches for a file that is called like test_this.cfg, rejecting its own name (test), the underscore and the file extension (.cfg). So the correct call would be:

$$ test this

This program was originally designed for a Linux environment, where the executable itself has no file extension on its own and therefore this call works. But now that I am on Windows, it has to be an .exe file, otherwise it is not being executed (Windows then asks what to do with this file). The PowerShell call then looks like this (the .\ are being added automatically by PowerShell):

$$ .\test this

but internally, the .exe extension seems to be there still, because the programm tells me that it cannot find a file called test.exe_this.cfg (of course it cannot, the name is test_this.cfg). Is there any way the PowerShell can simply ignore the .exe extension of the program, just like the default Windows CommandLine does?

Unfortunately, I'm not the program developer, so I cannot extend its functionalities to understand such patterns as well.

2 Answers

Bender the Greatest's helpful answer explains why a proper solution isn't possible without modifying the target program to account for the fact that executable files on Windows have filename extensions, typically .exe.

If you cannot modify the target program, here are potential workarounds:

  • Install WSL and directly run the Linux executable there; e.g.:

     wsl -e ./test this
    
  • Before running the Windows executable, create symlinks that effectively redirect file names such as test.exe_this.cfg to test_this.cfg

    • Note: Unless you have Developer Mode enabled in Windows, creating symlinks (symbolic links) requires elevation.

      # Create the symlink(s).
      Get-ChildItem test_*.cfg | ForEach-Object {
         New-Item -Type SymbolicLink ($_.Name -replace '^.[^_]+', '$&.exe') -Target $_.Name
      }
      
      # Now the target program should be able to find the right file
      # via its associated symlink.
      .\test this
      
  • Create a hard link to your executable whose file name has no extension, and invoke the hard link instead - see below.

    • Caveat: This relies on PowerShell interpreting an empty $env:PATHEXT entry to mean: consider extension-less file names executables. While this currently works (as of PowerShell 7.2.4), it is unclear whether this behavior is by design. By contrast, cmd.exe ignores empty $env:PATHEXT entries, so the solution won't work there.
# The path to your executable.
$originalExe = '.\test.exe'

# Create a temp. dir to house the temporary hard link.
$tmpDir = New-Item -Type Directory -Force (Join-Path ([IO.Path]::GetTempPath()) ([IO.Path]::GetRandomFileName()))

# Create the hard link, without a filename extension, which
# points to the same file data as the original executable.
$tmpExeWithoutExtension = 
  New-Item -Type HardLink `
           (Join-Path  $tmpDir ([IO.Path]::GetFileNameWithoutExtension($originalExe))) `
            -Target (Convert-Path $originalExe)

# Temporarily add an empty entry to $env:PATHEXT, so
# that PowerShell considers extension-less files executable.
$env:PATHEXT += ';'

# Invoke the hard link.
# The executable will now see the hard link's path as its own
# path in argv[0].
& $tmpExeWithoutExtension this

# Clean up.
$env:PATHEXT = $env:PATHEXT.Substring(0, $env:PATHEXT.Length-1)
Remove-Item -Recurse -LiteralPath $tmpDir

Note: The above creates the hard link on demand, and deletes it afterwards. You may choose to create a persistent hard link instead; if you choose to create it in the same directory as the original executable, you must invoke it by its full path in order to disambiguate it from the original executable, and another caveat applies there: it is also unclear whether this method of disambiguation works by design - see GitHub issue #7769.

No, because on Windows the file extension is still part of the file name. This is not a problem with PowerShell, it is simply how Windows execution works. The issue needs to be fixed in test.exe, the real problem is that test.exe has a bug on Windows when generating the file name that needs to be fixed.

Related