2

I can't get this batch script to work, even though I tried a lot of things, I'm now stuck.

The intention is for the batch script to re-run itself elevated, using PowerShell and Start-Process, if the admin check fails.

As long as the passed argument(s), do not contain spaces it works fine. However I need to forward the original batch arguments, (file paths), and can't figure out how to handle the case where they contain spaces.

Here is a small example of what I'm trying to achieve, in this example the echo doesn't correctly print out Arg 1 and Arg 2:

@echo off
setlocal enabledelayedexpansion

echo %1
echo %2

net session >nul 2>&1
if %ERRORLEVEL% neq 0 (
    echo Need to fix association but missing admin rights...
    echo Elevating privileges...

    REM Once it work, 'Arg 1' and 'Arg 2' will be replaced by %1 and %2 
    REM Which are gonna be path enclosed with double quote and containing space ie : "C:\Path to\A folder\"
    powershell -Command "$args = 'Arg 1', 'Arg 2'; Start-Process -Verb RunAs -FilePath '%~dpnx0' -ArgumentList $args"
    pause
    exit /b
)

pause
endlocal

2 Answers 2

3

There are multiple challenges:

  • When using the -Commmand (-c) parameter of powershell.exe, the Windows PowerShell CLI, any pass-through " characters, i.e. those that should be seen as part of the command for PowerShell to execute, must be escaped as \".

  • A long-standing bug in Start-Process requires that elements of an array passed to -ArgumentList that contain spaces be manually enclosed in embedded "..." in order to be passed correctly - see GitHub issue #5576

    • Note that the bug will not be fixed, so as not to break backward compatibility; a new parameter that effects the proper behavior has been proposed, but the discussion has stalled.

    • In light of the bug, it is usually conceptually clearer to use a single string with -ArgumentList that encodes all arguments, with embedded quoting as necessary, as shown below; see this answer for background information:

  • There are additional complications relating to batch files, specifically, which - in short - require calling your batch file indirectly, via cmd /c, plus an extra layer of embedded double-quoting.

Therefore, use the following (note that I'm omitting enabledelayedexpansion, as it isn't necessary here, and can cause problems with what should be verbatim ! characters):

@echo off
setlocal

net session >nul 2>&1 || (
    echo Need to fix association but missing admin rights...
    echo Elevating privileges...

    powershell -noprofile -Command $argStr = '\"%~1\" \"%~2\"'; Start-Process -Verb RunAs cmd.exe '/c', \"`\"`\"%~f0`\" $argStr`\"\"
    pause
    exit /b
)

echo Now running with elevation; arguments received:
echo [%1]
echo [%2]

pause
endlocal

Note:

  • %~f0 is a shorter alternative to %~dpnx0 for obtaining the running batch file's full file path.

  • %~1 and %~2 ensures that enclosing double quotes, if present, are stripped from the values of %1 and %2, so that these values can be double-quoted predictably as part of the PowerShell command, with the required \-escaping.

  • The fact that '...' is used around the value assigned to $argStr assumes that the value itself, specifically the values of %~1 and %~2, do not contain '; handling this case would require a bit more work.

Sign up to request clarification or add additional context in comments.

2 Comments

Works like a charm ! Thanks a lot, I would have never been able to find a solution, this required a lot of experience in batch scripting.
Glad to hear it, @Eledwin; my pleasure. Yeah, "quoting hell" is something I've been dealing with for a long time, both in cmd.exe and PowerShell.
-1

You must add an string interpolation for your arguments, So you must replace -ArgumentList $args by -ArgumentList '${args}'. Remember to include the single quotes.

You can get more information about it here:

2 Comments

It didn't work, my first echo display ${args}... I'm on windows 11 btw, don't know if it can be related to the issue.
With a variable name like args, {...} enclosure is optional, as discussed in the second linked post. Inside '...', string interpolation is by design not performed; in other words: you're passing verbatim ${args}, which is not the intent.

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.