2

Based on How can I auto-elevate my batch file, so that it requests from UAC administrator rights if required? I'm trying to make an RunElevated.bat (see code below) that accepts a command-line (batch file name and parameters) as arguments.

The RunElevated.bat works fine when the target batch file path has no spaces in them. But it fails as soon as that path has spaces: no matter how I quote things, either PowerShell barfs, or the parameters are not passed correctly from PowerShell to the batch file.

I tried:

  1. escaping with "" (as suggested by many sources)
  2. escaping with \" (as suggested by Escaping quotes in powershell.exe -command via command prompt)
  3. adding --% (as suggested by PowerShell and external commands done right and Easier Reuse of Command Lines From Cmd.exe)
  4. surrounding with ' (as suggested by CB.). So:

    • Is what I want to do possible at all?
    • If so: how?

RunElevated.bat:

:checkParameters
  echo [%*]
  if [%1]==[] ( goto :help ) else ( goto :checkPrivileges )

:help
  echo Syntax:
  echo   %0 CmdLine
  echo Where CmdLine is executed with UAC privileges.
  goto :exit

:checkPrivileges 
  net file 1>nul 2>nul
  if '%errorlevel%' == '0' ( goto :gotPrivileges ) else ( goto :getPrivileges )

:getPrivileges
  PowerShell "Start-Process -FilePath \"%0\" -Verb RunAs -ArgumentList \"%*\""
  goto :exit

:gotPrivileges
  %*
  pause

:exit
  pause
  exit /b 

echo-cd.bat which I stored in both D:\tools\echo-cd.bat and "D:\to ols\echo-cd.bat":

echo Current Directory: [%CD%]
echo Parameters: [%*]
pause

This runs fine:

D:\tools\RunElevated.bat D:\tools\echo-cd.bat foo

These fail:

D:\tools\RunElevated.bat "D:\to ols\echo-cd.bat" foo

First failure is at the %*:

C:\Windows\system32>D:\to ols\echo-cd.bat foo
'D:\to' is not recognized as an internal or external command,
operable program or batch file.

This is because PowerShell removed the quotes around the batch file name:

C:\Windows\system32>echo [D:\to ols\echo-cd.bat foo]
[D:\to ols\echo-cd.bat foo]

I expected D:\to ols\echo-cd.bat to have double quotes around it, and foo not.

D:\tools\RunElevated.bat \"D:\to ols\echo-cd.bat\" foo

Second failure is at the PowerShell level:

D:\>PowerShell "Start-Process -FilePath \"D:\tools\RunElevated.bat\" -Verb RunAs -ArgumentList \"\"D:\to ols\echo-cd.bat\" foo\""
Start-Process : Cannot validate argument on parameter 'ArgumentList'. The argument is null or empty. Supply an argument that is not null or empty and then try the command again.
At line:1 char:77
+ Start-Process -FilePath "D:\tools\RunElevated.bat" -Verb RunAs -ArgumentList <<<<  ""D:\to ols\echo-cd.bat" foo"

This is because escaping twice will end up with an empty string for ArgumentList.

D:\tools\RunElevated.bat --% "D:\to ols\echo-cd.bat" foo

Third failure is also at the %*:

C:\Windows\system32>--% D:\to ols\echo-cd.bat foo
'--%' is not recognized as an internal or external command,
operable program or batch file.

This too is because PowerShell removed the quotes around the batch file name:

C:\Windows\system32>echo [--% D:\to ols\echo-cd.bat foo]
[--% D:\to ols\echo-cd.bat foo]

I expected --% to be absent, D:\to ols\echo-cd.bat to have double quotes around it, and foo not.

D:\tools\RunElevated.bat '"D:\to ols\echo-cd.bat"' foo

Again a failure is at the %*:

C:\Windows\system32>'D:\to ols\echo-cd.bat' foo
The filename, directory name, or volume label syntax is incorrect.

This is because PowerShell removed the double quotes (and left the single quotes) around the batch file name:

C:\Windows\system32>echo ['D:\to ols\echo-cd.bat' foo]
['D:\to ols\echo-cd.bat' foo]

C:\Windows\system32>if ['D:\to] == [] (goto :help  )  else (goto :checkPrivileges  )
Community
  • 1
  • 1
Jeroen Wiert Pluimers
  • 23,965
  • 9
  • 74
  • 154
  • what about: `D:\tools\RunElevated.bat "'D:\to ols\echo-cd.bat\'" foo`? – CB. Jul 14 '14 at 12:46
  • @CB. that results in this error: `C:\Windows\system32>'D:\to ols\echo-cd.bat\' foo The filename, directory name, or volume label syntax is incorrect.`. With the last backslash removed, I get `C:\Windows\system32>'D:\to ols\echo-cd.bat' foo The filename, directory name, or volume label syntax is incorrect.` – Jeroen Wiert Pluimers Jul 14 '14 at 13:14
  • Yes sure... the last backslash was'nt would, forgot to delete it! Anyway try: `D:\tools\RunElevated.bat '"D:\to ols\echo-cd.bat"' foo` – CB. Jul 14 '14 at 13:20
  • @CB. that's the second error message in my above comment: it fails too (: I've added your suggestion to the list in my question for future readers. – Jeroen Wiert Pluimers Jul 14 '14 at 14:56

3 Answers3

1

I recall using the following approach for this:

start "" %systemroot%\System32\windowspowerShell\v1.0\powershell.exe -exec bypass -noprofile -command "&{ start-process powershell -verb RunAs -ArgumentList '-noprofile -exec bypass -file \"c:\temp\test folder\elevatedpowershell.ps1\"'}"

You can replace \"c:\temp\test folder\elevatedpowershell.ps1\" with \"$0\" like you did there. I also found the solution from Keith Hill on self elevating PowerShell to be useful if I need the person to be admin. I do not have the link now but it goes like this:

function IsAdministrator
{
    $Identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
    $Principal = New-Object System.Security.Principal.WindowsPrincipal($Identity)
    $Principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
}


function IsUacEnabled
{
    (Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System).EnableLua -ne 0
}

if (!(IsAdministrator))
{
    if (IsUacEnabled)
    {
        [string[]]$argList = @('-NoProfile', '-File', ('"' + $MyInvocation.MyCommand.Path + '"'))        
        $argList += $MyInvocation.BoundParameters.GetEnumerator() | Foreach {"-$($_.Key)", "$($_.Value)"}        
        $argList += $MyInvocation.UnboundArguments        
        Start-Process "$env:Windir\System32\WindowsPowerShell\v1.0\PowerShell.exe" -Verb Runas -WorkingDirectory $pwd -WindowStyle Hidden -ArgumentList $argList                        
        return
    }
    else
    {
        throw "You must be administrator to run this script"
    }
}

Assuming, you want to have elevated PowerShell script, you can have that at the top of your PowerShell script.

Adil Hindistan
  • 6,351
  • 4
  • 25
  • 28
0

Ugly but it can do the job.

Use single quotes (or any character of your choice) and insert this in your code:

:gotPrivileges
  cd "%~p0"
  set arg=%*
  set arg=%arg:'="%
  %arg%

::  %*
  pause
Ir Relevant
  • 963
  • 6
  • 12
0

If you have 8.3 file names enabled you can try this:

:getPrivileges
  setlocal enabledelayedexpansion
  set Args=%*
  for %%a in (%*) do if exist %%a (
            set Args=!Args:%%a="%%~sa"!
            )
  PowerShell "Start-Process -FilePath \"%0\" -Verb RunAs -ArgumentList \"%Args%\"" 2>nul
  goto :exit

However, if you pass an argument that happens to be the name of a file or folder (that exists relative to RunElevated.bat) it can get altered.

Ir Relevant
  • 963
  • 6
  • 12