1

For Python, I can run a remote script locally by the following command in PowerShell

(Invoke-WebRequest -Uri https://foo.bar.dev/script.py -UseBasicParsing).Content | python -

Can I do the same thing for a .bat file? Like

(Invoke-WebRequest -Uri https://foo.bar.dev/script.bat -UseBasicParsing).Content | pwsh -
Yue JIN
  • 1,047
  • 1
  • 10
  • 20
  • You could just get the content of the file, save it to a bat file in %temp% or something then run the file with start-process? Or if you used powershell instead, you could use it directly as base64 like: `# To use the -EncodedCommand parameter: $command = 'dir "c:\program files" ' $bytes = [System.Text.Encoding]::Unicode.GetBytes($command) $encodedCommand = [Convert]::ToBase64String($bytes) powershell.exe -encodedCommand $encodedCommand` – KG-DROID Aug 04 '23 at 11:40

1 Answers1

1

Batch files (.bat and .cmd) are interpreted by cmd.exe, the legacy command processor ("Command Prompt"), not by PowerShell.

You therefore cannot pipe the contents of a batch file directly to a PowerShell CLI (pwsh.exe for PowerShell (Core) 7+) , (powershell.exe for Windows PowerShell).

While you can pipe directly to cmd.exe, doing so has severe limitations and may or may not work as intended, depending on the content of the batch file - see this answer.

Thus, you'll have to save the downloaded batch-file content to a (temporary) file and execute it from there, which PowerShell can help you automate:

Invoke-RestMethod -Uri https://foo.bar.dev/script.bat -UseBasicParsing |
  ForEach-Object { 
      $tmpBatchFile = "$env:TEMP\~{0}_{1}.cmd" -f $PID, ([datetime]::Now.ToString('o') -replace '\D')
      Set-Content -Encoding OEM $tmpBatchFile -Value $_
      & $tmpBatchFile # Execute
      Remove-Item $tmpBatchFile
  }

Note:

  • Invoke-WebRequest was replaced with Invoke-RestMethod, which directly returns the data of interest and parses it, if appropriate; in the case of plain-text output, it is passed through as-is, as multiline text (after decoding into a .NET string).

  • It would be easy to create a function wrapper:

    • Precede the body of the ForEach-Object statement with
      function Invoke-AsBatchFile, for instance, to define a function by that name.

    • Replace $_ in the body with $input

    • You can then use that function in the pipeline:

      Invoke-RestMethod ... | Invoke-AsBatchFile
      

Note:

  • If you were to download the content of PowerShell scripts (*.ps1), you would execute it via [scriptblock]::Create() (as the more robust alternative to Invoke-Expression (iex)), which has the added advantage of being able to run in-process - but only if the script code doesn't contain exit statements. See this answer for details.
mklement0
  • 382,024
  • 64
  • 607
  • 775