1

Want to find equivalent of this under Linux in Bash

sh <(curl https://sample.com/cli.sh)

in Windows CLI or Powershell

I know curl comes installed in Windows. How can I achieve it?

Some people will say it is insecure. Also I will have a cli.ps1 with Windows Powershell.

dev
  • 1,119
  • 1
  • 11
  • 34
  • What can be insecure is piping a script directly to the shell like you're doing.. anyway, on windows you can use `Invoke-WebRequest` – Paolo Mar 28 '23 at 10:07
  • Will this work: Run powershell.exe ``` $cli = Invoke-WebRequest -Uri https://sample.com/cli.ps1 $cli.RawContent > cli.ps1 cli.ps1``` – dev Mar 28 '23 at 10:12
  • 1
    Please remove the edit from the question and add an answer – Paolo Mar 28 '23 at 11:20

2 Answers2

3

To offer more concise alternatives to your own effective solution - also assumes execution from PowerShell; the usual caveats about directly executing code downloaded from the web apply:[1]

powershell { iex (irm https://sample.com/cli.ps1) }

There are edge cases where the above malfunctions (see below); use the following robust form then, which also makes it easy to pass arguments to the script:

# More robust:
# Optionally pass arguments before the closing "}"
powershell { & ([scriptblock]::Create((irm https://sample.com/cli.ps1))) }

Assuming you know that the script contains no exit statements (which would exit your whole session), you can even use in-process execution (as your solution via an aux. file does), as follows:

# Note: Execution via & { ... } prevents iex (Invoke-Expression) from
#       effectively dot-sourcing the code, i.e. from running it
#       directly in the calling scope, which can have unwanted side effects.
iex "& { $(irm https://sample.com/cli.ps1) }"

# More robust:
& ([scriptblock]::Create((irm https://sample.com/cli.ps1))) 

In-process execution isn't just faster, it also enables a script to directly modify the current session's environment variables, and maintains full type fidelity with respect to the script's output objects, if any.
If the script does have exit statements, downloading to a (temporary) file first - as in your solution - is necessary, though the proposed future enhancement discussed below would make that unnecessary.

Note:

  • irm is a built-in alias of the Invoke-RestMethod cmdlet - see the next section for details.

  • iex is a built-in alias of the Invoke-Expression cmdlet.

    • Note that while Invoke-Expression is generally to be avoided, here it enables execution of source code without the need for an aux. script file (.ps1).

    • [scriptblock]::Create($sourceCodeString) is a way to parse source code into a script block (whose literal form is { ... }), i.e. a reusable unit of code that can be invoked on demand later, using either &, the call operator or ., the dot-sourcing operator.
      Its use bypasses the following Invoke-Expression bugs, present up to at least PowerShell 7.3.3.:

  • Since no script file is involved in the above commands, the effective execution policy does not apply.
    As an aside: If it did:

    • you could override it for a given CLI call with -ExecutionPolicy Bypass
    • you could set it persistently, using Set-ExecutionPolicy, as in your own answer - see this answer for background information.
  • Potential future enhancement:

    • GitHub issue #5909 discusses enhancing the Invoke-Command cmdlet to robustly support execution of scripts directly from the web, based on an Invoke-WebRequest call as pipeline input.

    • If this were implemented, a robust, in-process invocation would look like this (using the built-in aliases iwr and icm alias of Invoke-WebRequest and Invoke-Command, respectively):

      # WISHFUL THINKING as of PowerShell 7.3.3
      # Append -- arg1 ... to pass arguments.
      iwr https://sample.com/cli | icm
      

curl vs. curl.exe vs. Invoke-WebRequest vs. Invoke-RestMethod:

  • curl.exe indeed comes with recent versions of Windows.

  • To call it from Windows PowerShell, be sure to call it as curl.exe, i.e. with the .exe extension.

    • In Windows PowerShell, curl - without the .exe part - is an alias of the Invoke-WebRequest cmdlet, which is PowerShell's analog to curl.exe, albeit with very different syntax.
      Fortunately, this alias was removed from PowerShell (Core) 7+.
      However, a built-in, PowerShell-idiomatic alias that can be used in both PowerShell editions to refer to Invoke-WebRequest is iwr.
  • The related Invoke-RestMethod cmdlet - whose built-in alias is irm - can be used in lieu of Invoke-WebRequest in cases where you only care about the response's data, not also about a rich response-information object wrapping the data.

    • Note: Plain-text data - such as a script's source code in your case - is returned as-is, but for XML- and JSON-based data Invoke-RestMethod conveniently parses the data into an XML DOM in the form of an [xml] (System.Xml.XmlDocument) instance and a custom-object graph ([pscustomobject]), respectively.

[1] Only do this if you either fully control or implicitly trust the contents of the script, so as to prevent potentially malicious code from executing.

mklement0
  • 382,024
  • 64
  • 607
  • 775
2

This works:

set-ExecutionPolicy RemoteSigned -Scope CurrentUser
$cli = Invoke-WebRequest -Uri https://server.com/cli-win.txt
$cli.Content > cli.ps1
./cli.ps1
mklement0
  • 382,024
  • 64
  • 607
  • 775
dev
  • 1,119
  • 1
  • 11
  • 34