Any way to achieve what 'curl -s -w %{redirect_url} "some_url"' does with 'Invoke-WebRequest'?

huangapple go评论62阅读模式
英文:

Any way to achieve what 'curl -s -w %{redirect_url} "some_url"' does with 'Invoke-WebRequest'?

问题

$url = "https://api.adoptium.net/v3/binary/latest/19/ga/linux/x64/jdk/hotspot/normal/eclipse"
$response = Invoke-WebRequest -Uri $url -UseBasicPars‌ing

if ($response.StatusCode -eq 200) {
    $fetch_url = $response.Headers['Location']
    $fname = $response.Headers['Content-Disposition'] -replace '.*filename="([^"]*)".*', '$1'
} else {
    $fetch_url = $response.Content
    $fname = "No releases match the request"
}

$fetch_url
$fname

This PowerShell script performs the same actions as the provided Bash script, obtaining the download URL and filename. It uses Invoke-WebRequest to make the request and handles the different responses accordingly. The $fetch_url variable will contain the actual download URL, and the $fname variable will hold the filename.

Please note that you need to have appropriate error handling and further actions based on the response status code, similar to what was done in the Bash script.

英文:

I am trying to convert following from bash to PowerShell:

url="https://api.adoptium.net/v3/binary/latest/19/ga/linux/x64/jdk/hotspot/normal/eclipse"
fetch_url=$( curl -s -w %{redirect_url} "${url}" )
fname=$( curl -OLs -w %{filename_effective} "${fetch_url}" )

Executing the above with bash sets $fetch_url to https://github.com/adoptium/temurin19-binaries/releases/download/jdk-19.0.2+7/OpenJDK19U-jdk_x64_linux_hotspot_19.0.2_7.tar.gz or, if there is no URL available, to {"errorMessage":"No releases match the request"} and the command returns with a return code other than "0".

Executing the next line $fname will be OpenJDK19U-jdk_x64_linux_hotspot_19.0.2_7.tar.gz. The file is downloaded to this name, because curl was called with -OLs. (-O: output to name found within url; -L: follow redirects; -s: silent; -w %{filename_effective}: selects the resulting filename to be reported on stdout). $fname then holds this filename for later use.

Any idea how to make the same happen with Invoke-WebRequest? For the URL given this only returns

> Invoke-WebRequest -uri "https://api.adoptium.net/v3/binary/latest/19/ga/linux/x64/jdk/hotspot/normal/eclipse"
StatusCode        : 200
StatusDescription : OK
Content           : {31, 139, 8, 0…}
RawContent        : HTTP/1.1 200 OK
Connection: keep-alive
ETag: "0x8DAFB9194B2EB0B"
Server: Windows-Azure-Blob/1.0
Server: Microsoft-HTTPAPI/2.0
x-ms-request-id: a6f360ad-e01e-004e-7f8b-470ced000000
x-ms-version: …
Headers           : {[Connection, System.String[]], [ETag, System.String[]], [Server, System.String[]], [x-ms-request-id, System.String[]]…}
RawContentLength  : 200079543
RelationLink      : {}

There seems to be no way to find out the real download-url and filename as with curl. I've tried with ($curl set to C:\cygwin\bin\curl.exe):

$fetch_url = &$curl -s -w %{redirect_url} "$api_url"

Instead of using Invoke-WebRequest. But this leads the server asking for authentication -- not what was intended. Any idea how to make one or the other work as expected?

答案1

得分: 1

现代的Windows版本现在附带了curl.exe,因此您可以简单地将您的Bash脚本转换为PowerShell:

$url = 'https://api.adoptium.net/v3/binary/latest/19/ga/linux/x64/jdk/hotspot/normal/eclipse'
$fetch_url = curl.exe -s -w '%{redirect_url}' $url
$fname= curl.exe -OLs -w '%{filename_effective}' $fetch_url

# === 用于诊断输出和错误处理的额外代码。

# 显示变量的值。
[pscustomobject] @{
  URL = $url
  FetchUrl = $fetch_url
  FileName = $fname
} | Format-List

# 如果最后一个curl调用表示失败,则抛出错误。
if ($LASTEXITCODE) { throw "curl失败,退出码为 $LASTEXITCODE。" }

注意:

  • 在Windows PowerShell中,请确保使用curl.exe,即包括文件扩展名,以绕过内置的curl别名,该别名指的是Invoke-WebRequest - 这个问题已经在PowerShell (Core) 7+中修复。

  • %{...}参数用'...'括起来,因为{}在PowerShell中是元字符(它们用于括住脚本块文字,{...})。

另外,您还可以使用Invoke-WebRequest来实现您的任务,但这更加繁琐。以下是一个示例:

$url = 'https://api.adoptium.net/v3/binary/latest/19/ga/linux/x64/jdk/hotspot/normal/eclipse'

# 从响应头的'Location'字段中获取(即时)重定向URL。
# 请注意需要使用-MaximumRedirection 0来抑制Invoke-WebRequest默认执行的自动重定向。
$fetch_url = (Invoke-WebRequest -Method HEAD -MaximumRedirection 0  $url).Headers.Location

# 从重定向URL中提取最后一部分并将其用作文件名。
$fname = Split-Path -Leaf $fetch_url

# 执行下载,静默进行。
& {
  $ProgressPreference = 'SilentlyContinue'
  Invoke-WebRequest -ErrorAction Stop -OutFile $fname $fetch_url
}

这里提供了两种不同版本的PowerShell脚本,您可以根据需要选择其中一种。

英文:

<!-- language-all: sh -->

It is worth noting that modern Windows versions now come with curl.exe, so you could simply translate your Bash script to PowerShell:

$url = &#39;https://api.adoptium.net/v3/binary/latest/19/ga/linux/x64/jdk/hotspot/normal/eclipse&#39;
$fetch_url = curl.exe -s -w &#39;%{redirect_url}&#39; $url
$fname= curl.exe -OLs -w &#39;%{filename_effective}&#39; $fetch_url

# === Extra code for diagnostic output and error handling.

# Show the resulting variable values.
[pscustomobject] @{
  URL = $url
  FetchUrl = $fetch_url
  FileName = $fname
} | Format-List

# Throw an error, if the last curl call indicated failure.
if ($LASTEXITCODE) { throw &quot;curl failed with exit code $LASTEXITCODE.&quot; }

Note:

  • In Windows PowerShell, be sure to use curl.exe, i.e. include the filename extension, so as to bypass the built-in curl alias that refers to Invoke-WebRequest - this problem has been fixed in PowerShell (Core) 7+.

  • The %{…} arguments are enclosed in &#39;…&#39;, because { and } are metacharacters in PowerShell (they enclose script-block literals, {…})


It is possible to implement your task using Invoke-WebRequest, but it is more cumbersome:

  • Windows PowerShell solution:
$url = &#39;https://api.adoptium.net/v3/binary/latest/19/ga/linux/x64/jdk/hotspot/normal/eclipse&#39;

# Get the (immediate) redirection URL from the response header&#39;s &#39;Location&#39; field.
# Note the need for -MaximumRedirection 0, to suppress the automatic
# redirection that Invoke-WebRequest performs *by default*.
$fetch_url = (Invoke-WebRequest -Method HEAD -MaximumRedirection 0  $url).Headers.Location

# Extract the last component from the redirection URL and use it as the file name.
$fname = Split-Path -Leaf $fetch_url

# Perform the download, silently.
&amp; {
  $ProgressPreference = &#39;SilentlyContinue&#39;
  Invoke-WebRequest -ErrorAction Stop -OutFile $fname $fetch_url
}
  • PowerShell (Core) 7+ solution (verified in v7.3.2):

    • Unfortunately, the Invoke-WebRequest call requires extra work, due to changes in behavior:

      • A 302 HTTP status code, indicating redirection, is now considered a (statement-terminating) error by default, which must be suppressed with -SkipHttpErrorCheck in order to get the header data returned.

      • Even with -SkipHttpErrorCheck, a non-terminating error is also emitted, stating The maximum redirection count has been exceeded [...], which can be silenced with -ErrorAction Ignore.

      • Header field values are now [string[]] typed. Given that only one redirection URL is present, simply accessing element [0] is sufficient.

      • Official list of all changes

    • Therefore, replace the first Invoke-WebRequest call above with the following:

$fetch_url = (Invoke-WebRequest -Method HEAD -MaximumRedirection 0 -SkipHttpErrorCheck -ErrorAction Ignore $url).Headers.Location[0]

Taking a step back:

  • Targeting the ultimately redirected-to URL returns a Content-Disposition header that contains a suggested download file name:

    attachment; filename=OpenJDK19U-jdk_x64_linux_hotspot_19.0.2_7.tar.gz
    
  • You can let curl use this file name by combining the -O (--remote-name option with -J (--remote-header-name)

    • Note: The suggested file name happens to be the same as the last segment of the (immediate) redirection URL in the case at hand, but there is no guaranteed relationship between the two.

    • The curl man page contains a warning regarding -J:

> Exercise judicious use of this option, especially on
Windows. A rogue server could send you the name of a DLL or
other file that could be loaded automatically by Windows or some
third party software.

If you want to take advantage of curl's -J option, your code can be simplified to:

# Directly saves to file `OpenJDK19U-jdk_x64_linux_hotspot_19.0.2_7.tar.gz` 
# and prints its name.
$fname = curl.exe -sLJO -w &#39;%{filename_effective}&#39; $url

Note:

  • Invoke-WebRequest has no support for honoring a Content-Disposition field.

    • You can extract the file name from it manually, in a separate step, as follows:

      (Invoke-WebRequest $url -Method Head).Headers[&#39;Content-Disposition&#39;] -replace &#39;^.*\bfilename\*?=&quot;?([^;&quot;]+)?.*&#39;, &#39;$1&#39;
      
  • In fact, up to at least PowerShell 7.3.2, it doesn't even have support for using the URL's last segment (no equivalent to curl's -O): a file name must explicitly be passed to the -OutFile parameter:

    • However, GitHub PR #19007 will bring such support to a future PowerShell version, namely the ability to specify only a directory path to -OutFile, with the file name being implied by the last segement of the URL, albeit of the redirected-to URL (unlike what curl's -O does when combined with -L)

答案2

得分: 0

这是一个小小的技巧好奇是否有更好的方法

$url = "https://api.adoptium.net/v3/binary/latest/19/ga/linux/x64/jdk/hotspot/normal/eclipse"
$response = Invoke-WebRequest -Uri $url -UseBasicParsing -Method HEAD

$null = $response.RawContent -match 'filename=([^\r\n]*)'
$fileName = $matches[1]
Invoke-WebRequest -Uri $url -UseBasicParsing -OutFile "C:\temp$fileName"
英文:

A little bit of a hack, curious to see if anyone has a better approach

$url = &quot;https://api.adoptium.net/v3/binary/latest/19/ga/linux/x64/jdk/hotspot/normal/eclipse&quot;
$response = Invoke-WebRequest -Uri $url -UseBasicParsing -Method HEAD

$null = $response.RawContent -match &#39;filename=([^\r\n]*)&#39;
$fileName = $matches[1]
Invoke-WebRequest -Uri $url -UseBasicParsing -OutFile &quot;C:\temp$fileName&quot;

huangapple
  • 本文由 发表于 2023年2月23日 23:18:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/75546788.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定