Why does powershell think $dir
is null
when setting the location but not when writing the output?
$command = {
param($dir)
Set-Location $dir
Write-Output $dir
}
# run the command as administrator
Start-Process powershell -Verb RunAs -ArgumentList "-NoExit -Command $command 'C:\inetpub\wwwroot'"
This results in the following output:
Set-Location : Cannot process argument because the value of argument "path" is null. Change the value of argument
"path" to a non-null value.
At line:3 char:2
+ Set-Location $dir
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Set-Location], PSArgumentNullException
+ FullyQualifiedErrorId : ArgumentNull,Microsoft.PowerShell.Commands.SetLocationCommand
C:\inetpub\wwwroot
I also tried:
$command = {
param($dir)
Set-Location $dir
Write-Output $dir
}
$outerCommand = {
Invoke-Command -ScriptBlock $command -ArgumentList 'C:\inetpub\wwwroot'
}
# run the command as administrator
Start-Process powershell -Verb RunAs -ArgumentList "-NoExit -Command $outerCommand"
But then I got:
Invoke-Command : Cannot validate argument on parameter 'ScriptBlock'. The argument is null. Provide a valid value for
the argument, and then try running the command again.
At line:2 char:30
+ Invoke-Command -ScriptBlock $command 'C:\inetpub\wwwroot'
+ ~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Invoke-Command], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.InvokeCommandCommand
Possible clue: if I set a local variable instead of using a param, it works perfectly:
$command = {
$dir = 'C:\inetpub\wwwroot'
Set-Location $dir
Write-Output $dir
}
# run the command as administrator
Start-Process powershell -Verb RunAs -ArgumentList "-NoExit -Command $command"
Similar Q/As that didn't quite answer my question:
Start-Process
, which I need to run as administrator)Invoke-Command
rather than Start-Process
)You can run scripts with parameters in any context by simply specifying them while running the PowerShell executable like powershell.exe -Parameter 'Foo' -Parameter2 'Bar' . Once you open cmd.exe, you can execute a PowerShell script like below.
The call operator is another way to execute script blocks stored in a variable. Like Invoke-Command , the call operator executes the script block in a child scope. The call operator can make it easier for you to use parameters with your script blocks.
The Start-Process cmdlet starts one or more processes on the local computer. By default, Start-Process creates a new process that inherits all the environment variables that are defined in the current process.
Running a PowerShell script from the Command Prompt If you would like to run a PowerShell script in CMD, you'll need to execute it by calling the PowerShell process with the -File parameter, as shown below: PowerShell -File C:\TEMP\MyNotepadScript. ps1. PowerShell -File C:\TEMP\MyNotepadScript.
$command
is a script block ({ ... }
) and stringifying a script block results in its literal contents, excluding the enclosing {
and }
.
Therefore, your expandable string "-NoExit -Command $command 'C:\inetpub\wwwroot'"
literally expands to the following string - note the missing { ... }
around the original script block:
-NoExit -Command
param($dir)
Set-Location $dir
Write-Output $dir
'C:\inetpub\wwwroot'
Due to the loss of the enclosing {
and }
, the new powershell
process spawned by Start-Process
quietly ignored the orphaned param($dir)
statement and, given that the new process therefore had no $dir
variable (given that it isn't an automatic variable either), the command failed, because Set-Location $dir
was tantamount to Set-Location $null
, which fails.[1]
Note that you can never pass script blocks as such to Start-Process
- all arguments must be strings, because only strings can be passed to external processes.
The simplest solution in your case is to:
enclose the $command
reference in your expandable string in { ... }
to compensate for the loss of this enclosure due to stringification
and prepend &
to ensure invocation of the resulting script block in the new process.
Here's a working solution:
Start-Process powershell -Verb RunAs -ArgumentList `
"-NoExit -Command & { $command } 'C:\inetpub\wwwroot'"
Important: While not needed with the specific script block at hand, any script block that has embedded "
chars. wouldn't be parsed correctly as part of the overall "..."
string by the target process; to prevent this, they must be escaped as \"
, which is what external programs expect - including PowerShell via its CLI:
# Script block with embedded " chars.
$command = {
param($dir)
Set-Location $dir
"Current dir: $dir"
}
# Embed the script block with " escaped as \"
Start-Process powershell -Verb RunAs -ArgumentList `
"-NoExit -Command & { $($command -replace '"', '\"') } 'C:\inetpub\wwwroot'"
[1] It fails in Windows PowerShell; in PowerShell Core, it is tantamount to Set-Location
without arguments, which changes to the current user's home folder.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With