2

I need to run this command, which is read from another script.

$command = "$arr = @($input)
$arr.count
Write-Host $arr[0]"

"Classical Music for When You are on a Deadline." | & cmd /C powershell -Command {$command}

So I am getting something through the pipe and then I use it in my string command. The code does not work because $command is not expanded to the string inside the call and therefore unknown in the launched powershell command.

These work as expected, but the commands are not taken from a string:

"Classical Music for When You are on a Deadline." | & cmd /C powershell -Command {Invoke-Expression "Write-Host $input"}

# No:   System.Management.Automation.Runspaces.PipelineReader`1+<GetReadEnumerator>d__20[System.Object]
# "Classical Music for When You are on a Deadline." | & cmd /C powershell -Command {Write-Host $input}

"Classical Music for When You are on a Deadline." | & cmd /C powershell -Command {$arr = @($input)
    $arr.count
    Write-Host $arr[0]}

2 Answers 2

2
  • { $command } does not turn the value of string $command into a script block - it simply creates a script block that references a variable named $command.

  • Also, do not call powershell.exe via cmd /c - it is generally unnecessary.

Therefore:

$command = '$arr = @($input)
$arr.count
Write-Host $arr[0]'

"Classical Music for When You are on a Deadline." | 
  powershell ([scriptblock]::Create($command))  # -Command is implied.

Note:

  • The above is the dynamic, variable-based equivalent of the last solution attempt in your question.

  • Using a script block rather than a string with PowerShell's CLI - which is only an option from inside PowerShell - is generally preferable, for the following reasons:

    • Limited type fidelity is provided, using the same XML-based serialization infrastructure as for background jobs and remoting; that is, you can return objects other than strings from such a CLI call; similarly, such calls preserve all of PowerShell's output streams.

    • Base64-encoding is used behind the scenes to safely pass the script block's source code to the target PowerShell process, which bypasses any quoting and escaping headaches.

      • Notably, due to a long-standing bug up to PowerShell 7.2.x, passing commands as string requires embedded " characters to be manually escaped as \" - see this answer.

In other words: While powershell -Command $command would have worked with the specific sample string, it would fail if the string contained " characters, invariably in Windows PowerShell, and up to at least PowerShell (Core) 7.2.x.


Taking a step back: Since you're already running in PowerShell, there is no need to create another instance, as a child process, which is expensive.

$command = '$arr = @($input)
$arr.count
Write-Host $arr[0]'

# Invoke the dynamically created script block directly.
"Classical Music for When You are on a Deadline." | 
  & ([scriptblock]::Create($command))

Note:

  • &, the call operator is used to invoke the script block, which runs it in a child scope.

    • If you want the script block to run directly in the caller's scope, use ., the dot-sourcing operator instead.
  • Invoke-Expression (iex) - which should generally be avoided - is not an option here anyway, because it doesn't support using the pipeline to pass data to the code being evaluated.

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

4 Comments

Thanks, @SantiagoSquarzon: it would, but only if $command happens to contain no embedded " characters - I've updated the answer to make that clear.
I see what you mean, if calling from pwsh works ok using a here-string tho it fails when calling from powershell
@SantiagoSquarzon, no, unless you happen to be on v7.3.0 specifically, it won't work from pwsh either - and it looks like the v7.3.0 fix will be reverted and become opt-in in v7.3.1+ - see github.com/PowerShell/PowerShell/issues/18694
Unfortunately, I didn't tell the whole story: cmd /c is mandatory because that's what the (missing here) final command is expecting. My bad, OTOH, the first solution works very well for me with cmd /C powershell, so your generous answer solves my specific problem, while covering a more generic problem. Thank you!
0

you can use the Invoke-Expression cmdlet to run a variable string as a command in PowerShell. For example: Invoke-Expression $commandString

1 Comment

Invoke-Expression (iex) should generally be avoided, which is always worth mentioning. While a security risk is inherent in the scenario at hand - execution of a command stored in a string received from an outside source - a solution without Invoke-Expression is still possible and preferable in this case.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.