powershell 有什么方法可以实现'curl -s -w %{redirect_url}“some_url”'对'Invoke-WebRequest'的作用吗?

ryoqjall  于 2023-03-18  发布在  Shell
关注(0)|答案(2)|浏览(94)

我正在尝试将以下内容从bash转换为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}" )

使用bash执行上面的命令会将$fetch_url设置为https://github.com/adoptium/temurin19-binaries/releases/download/jdk-19.0.2+7/OpenJDK19U-jdk_x64_linux_hotspot_19.0.2_7.tar.gz,或者如果没有可用的URL,则设置为{"errorMessage":"No releases match the request"},并且命令返回一个非“0”的返回代码。
执行下一行$fname将是OpenJDK19U-jdk_x64_linux_hotspot_19.0.2_7.tar.gz。文件将下载到此名称,因为curl是用-OLs调用的。(-O:输出到url中找到的名称;-L:遵循重定向;-s:无声;-w %{filename_effective}:选择要在stdout上报告的结果文件名)。$fname然后保存此文件名以供以后使用。
你知道如何用Invoke-WebRequest实现同样的效果吗?对于给定的URL,它只返回

> 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      : {}

似乎没有办法像curl那样找到真实的的下载URL和文件名。我试过($curl设置为C:\cygwin\bin\curl.exe):

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

而不是使用Invoke-WebRequest。但是这会导致服务器请求身份验证--这不是预期的。有什么想法可以让其中一个按预期工作吗?

juzqafwq

juzqafwq1#

有点黑客的味道,想看看有没有更好的方法

$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"
mqxuamgl

mqxuamgl2#

值得注意的是,现代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

# === 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 "curl failed with exit code $LASTEXITCODE." }

注:

  • Windows PowerShell 中,确保使用curl.exe,即包含文件扩展名,以便绕过指向Invoke-WebRequest的内置curl * 别名 *-此问题已在PowerShell (Core) 7+中修复。
  • %{…}参数包含在'…'中,因为{}在PowerShell中是 * 元字符 *(它们包含script-block文本{…}

使用Invoke-WebRequest可以实现您的任务,但它更麻烦

  • Windows PowerShell 解决方案:
$url = 'https://api.adoptium.net/v3/binary/latest/19/ga/linux/x64/jdk/hotspot/normal/eclipse'

# Get the (immediate) redirection URL from the response header's 'Location' 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.
& {
  $ProgressPreference = 'SilentlyContinue'
  Invoke-WebRequest -ErrorAction Stop -OutFile $fname $fetch_url
}
  • PowerShell (Core) 7+解决方案(在v7.3.2中验证):
  • 遗憾的是,由于行为的更改,**Invoke-WebRequest**调用需要额外的工作:
  • 302 HTTP状态代码表示重定向,现在默认情况下被视为(语句终止)error,必须使用-SkipHttpErrorCheck抑制该代码才能返回头数据。
  • 即使使用-SkipHttpErrorCheck,也会发出一个非终止错误,声明The maximum redirection count has been exceeded [...],可以使用-ErrorAction Ignore将其静音。
  • 头字段值现在是[string[]]类型的。假定只有 * 一个 * 重定向URL存在,简单地访问元素[0]就足够了。
  • 所有变更的官方列表
  • 因此,请将上面的 firstInvoke-WebRequest调用替换为以下内容:
$fetch_url = (Invoke-WebRequest -Method HEAD -MaximumRedirection 0 -SkipHttpErrorCheck -ErrorAction Ignore $url).Headers.Location[0]

后退一步

  • 以最终重定向到的URL为目标返回**Content-Disposition标头**,其中包含 * 建议的下载文件名*:
attachment; filename=OpenJDK19U-jdk_x64_linux_hotspot_19.0.2_7.tar.gz
  • 通过将-O--remote-name选项与-J--remote-header-name)组合使用,可以让curl使用此文件名
  • 注:在当前情况下,建议的文件名 * 恰好与(直接)重定向URL的最后一段相同,但两者之间没有保证的关系。
    curlman页面包含关于-J的 * 警告

请谨慎使用此选项,尤其是在Windows上。恶意服务器可能会向您发送Windows或某些第三方软件可以自动加载的DLL或其他文件的名称。
如果您想利用curl-J选项,您的代码可以简化为:

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

注:

  • Invoke-WebRequest * 不 * 支持遵循Content-Disposition字段。
  • 您可以在单独的步骤中 * 手动 * 从中提取文件名,如下所示:
(Invoke-WebRequest $url -Method Head).Headers['Content-Disposition'] -replace '^.*\bfilename\*?="?([^;"]+)?.*', '$1'
  • 事实上,至少在PowerShell 7.3.2之前,它甚至不支持使用URL的最后一段(不等同于curl-O):文件名必须 * 显式地 * 传递给-OutFile参数:
  • 然而,GitHub PR #19007将为 * 未来 * 的PowerShell版本带来这样的支持,即只指定-OutFile的 * 目录 * 路径的能力,文件名由URL的最后一段暗示,尽管是 * 重定向到 * URL(不像curl-O-L组合时所做的那样)

相关问题