1

Long time reader but new poster. I've been wrestling with this issue for an entire day and it's driving me nuts. After scouring this site and Google, I'm still stuck. Basically, I can't figure out how to implement a "sudo" in Powershell that works with the Copy-Item CmdLet. Here's my code:

PROFILE.PS1:

# Trigger UAC to elevate the supplied command
function Elevate-Process() {
    if($args.Length -eq 0) {
        error ('USAGE: ' + $MyInvocation.InvocationName + ' <executable> [arg1 arg2 arg3 ...]');
    } else {
        try {
            if($args.Length -eq 1) {
                Start-Process $args[0] -Verb RunAs;
            } else {
               Start-Process $args[0] -ArgumentList $args[1..$args.Length] -Verb RunAs;
            }
        } catch {
            error $_.Exception.Message;
        }
    }
}

# Display pretty-formatted error messages
function error() {
    # Validate function parameters
    Param(
        [Parameter(Mandatory=$true)]
        [ValidateScript({$_.GetType().Name -eq 'String'})]
        $sMessage
    );

    Write-Host $sMessage -BackgroundColor Yellow -ForegroundColor Red;
}

# Set aliases
set-alias sudo Elevate-Process;

APPLY-INI.PS1:

sudo Copy-Item '.\standard_menu.ini' 'C:\Program Files (x86)\Opera\ui\';
#sudo [System.IO.File]::Copy '.\webmailproviders.ini' 'C:\Program Files (x86)\Opera\defaults\webmailproviders.ini' $true;

Write-Host 'Press any key to exit...';
$host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null;

When I execute powershell.exe .\apply-ini.ps1, I get the following error:

This command cannot be executed due to the error: The system cannot find the file specified.

I've tried EVERYTHING I could possibly find without any luck. Hopefully someone can point me in the right direction.

Eric
  • 230
  • 3
  • 9

2 Answers2

2

There are several subtleties to getting a good invoke-elevated command implemented. This is the current implementation of Invoke-Elevated that we use in the PowerShell Community Extensions (2.1 Beta). You might find it useful in gettings yours to work or you could just grab PSCX 2.1 Beta. :-)

 Invoke-Elevated
    Opens a new PowerShell instance as admin.
.EXAMPLE
    C:\PS> Invoke-Elevated Notepad C:\windows\system32\drivers\etc\hosts
    Opens notepad elevated with the hosts file so that you can save changes to the file.
.EXAMPLE
    C:\PS> Invoke-Elevated {gci c:\windows\temp | export-clixml tempdir.xml; exit}
    Executes the scriptblock in an elevated PowerShell instance.
.EXAMPLE
    C:\PS> Invoke-Elevated {gci c:\windows\temp | export-clixml tempdir.xml; exit} | %{$_.WaitForExit(5000)} | %{Import-Clixml tempdir.xml}
    Executes the scriptblock in an elevated PowerShell instance, waits for that elevated process to execute, then
    retrieves the results.
.NOTES
    Aliases:  su
    Author:   Keith Hill
#>
function Invoke-Elevated 
{
    Write-Debug "`$MyInvocation:`n$($MyInvocation | Out-String)"

    $startProcessArgs = @{
        FilePath     = "PowerShell.exe"
        ArgumentList = "-NoExit", "-Command", "& {Set-Location $pwd}"
        Verb         = "runas"
        PassThru     = $true
        WorkingDir   = $pwd
    }

    $OFS = " "
    if ($args.Count -eq 0)      
    {
        Write-Debug "  Starting Powershell without no supplied args"
    }
    elseif ($args[0] -is [Scriptblock]) 
    {
        $script  = $args[0]
        $cmdArgs = $args[1..$($args.Length)]
        $startProcessArgs['ArgumentList'] = "-NoExit", "-Command", "& {Set-Location $pwd; $script}"
        if ($cmdArgs.Count -gt 0)
        {
            $startProcessArgs['ArgumentList'] += $cmdArgs
        }
        Write-Debug "  Starting PowerShell with scriptblock: {$script} and args: $cmdArgs"
    }
    else
    {
        $app     = Get-Command $args[0] | Select -First 1 | ? {$_.CommandType -eq 'Application'}
        $cmdArgs = $args[1..$($args.Length)]
        if ($app) {
            $startProcessArgs['FilePath'] = $app.Path
            if ($cmdArgs.Count -eq 0)
            {
                $startProcessArgs.Remove('ArgumentList')
            }
            else
            {
                $startProcessArgs['ArgumentList'] = $cmdArgs
            }
            Write-Debug "  Starting app $app with args: $cmdArgs"
        }
        else {
            $poshCmd = $args[0]
            $startProcessArgs['ArgumentList'] = "-NoExit", "-Command", "& {Set-Location $pwd; $poshCmd $cmdArgs}"
            Write-Debug "  Starting PowerShell command $poshCmd with args: $cmdArgs"
        }
    }

    Write-Debug "  Invoking Start-Process with args: $($startProcessArgs | Format-List | Out-String)" 
    Microsoft.PowerShell.Management\Start-Process @startProcessArgs
}
Keith Hill
  • 194,368
  • 42
  • 353
  • 369
  • Wow thanks for the tip. However, when using Invoke-Elevated, I get the following errors: – Eric Jan 02 '12 at 21:25
  • `Set-Location : A positional parameter cannot be found that accepts argument 'Customizations'. At line:1 char:16 + & {Set-Location <<<< C:\Users\Eric\Documents\Opera Customizations; cp .\standard_menu.ini C:\Pro gram Files (x86)\Opera\ui\standard_menu.ini} + CategoryInfo : InvalidArgument: (:) [Set-Location], ParameterBindingException + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.SetLocatio nCommand` – Eric Jan 02 '12 at 21:27
  • `The term 'x86' is not recognized as the name of a cmdlet, function, script file, or operable progra m. Check the spelling of the name, or if a path was included, verify that the path is correct and t ry again. At line:1 char:107 + & {Set-Location C:\Users\Eric\Documents\Opera Customizations; cp .\standard_menu.ini C:\Program F iles (x86 <<<< )\Opera\ui\standard_menu.ini} + CategoryInfo : ObjectNotFound: (x86:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException` – Eric Jan 02 '12 at 21:28
  • The code I'm executing is as follows: `Invoke-Elevated cp '.\standard_menu.ini' 'C:\Program Files (x86)\Opera\ui\standard_menu.ini';` – Eric Jan 02 '12 at 21:30
  • Put the command in a scriptblock e.g. `Invoked-Elevated {cp src target}` and it will work. The final else clause is supposed to handle this but the quotes around the target path are getting stripped off. – Keith Hill Jan 02 '12 at 23:14
  • Didn't work. I give up. Powershell has wasted far too much of my time. Thanks for your help tho. – Eric Jan 03 '12 at 01:01
-1

You are invoking Start-Process with a cmdlet (copy-item) as its filepath argument instead of the filepath of an executable file.

sudo xcopy '.\standard_menu.ini' 'C:\Program Files (x86)\Opera\ui\';

will do the trick.

jon Z
  • 15,838
  • 1
  • 33
  • 35