48413


例えば [curlリリースページ](https://github.com/curl/curl/releases)からリンクされているファイル
`curl-7.61.1.tar.bz2.asc` をダウンロードしようとしたとき、

[code]

    curl -L -O \
    https://github.com/curl/curl/releases/download/curl-7_61_1/curl-7.61.1.tar.bz2.asc
    
[/code]

とすればカレントディレクトリに `curl-7.61.1.tar.bz2.asc` というファイル名でダウンロードされますが、このURLの末尾に
`#/custom-named-file` を付与して

[code]

    curl -L -O \
    https://github.com/curl/curl/releases/download/curl-7_61_1/curl-7.61.1.tar.bz2.asc#/custom-named-file
    
[/code]

とすると `custom-named-file` というファイル名でダウンロードされます。

これは何か公の仕様に則った挙動なのでしょうか(その場合何かこの仕様を調べるのに利用できる単語/キーワードはあるでしょうか)。  
あるいはcurlの独自機能なのでしょうか。

* * *

(補足)  
本件に疑問を持った発端は、 PowerShell 5.1で `WebRequest` を利用する場合も上記curlと同じような挙動を示すのに対し、
PowerShell Core 6.1.0 では そのようなURLを渡すとステータスコード400エラー(Bad
Request)になるのを見つけたことからです([参考](https://github.com/lukesampson/scoop/issues/2382))。

* * *

「正当」という言葉の意味が曖昧だ、という[コメント](https://ja.stackoverflow.com/questions/48409/curl%e3%81%a7url%e3%81%ab-%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab%e5%90%8d-%e3%82%92%e4%bb%98%e4%b8%8e%e3%81%97%e3%81%a6%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab%e5%91%bd%e5%90%8d%e3%81%a7%e3%81%8d%e3%82%8b%e6%8c%99%e5%8b%95%e3%81%ae%e6%ad%a3%e5%bd%93%e6%80%a7#comment50106_48409)を頂きましたので追記致します。

Web系開発者から見て、curlの今回の挙動は、当然そうなるだろうというものなのか、いやいやおかしいだろうというものなのかが知りたいです。  
(そしてそう考える根拠がもしあるのなら、それも知りたいです)

また本件について自分でも調べようとしたのですが、 `#/`
を検索キーワードとして入力しても有用な情報が何も得られないので、もし呼び方があるのなら教えて下さい。

「補足」で触れていますが、PowrShell5.1では `#/name` を受け入れ6.1ではエラーとなる、という挙動に気付いたのが発端なのですが、  
`#/name`
ってcurlでちゃんと処理できてるじゃん(PowerShell6バグってんのかよ)…ん？そもそもこれって何なんだっけ？、というのが質問の根本です。


CC BY-SA 4.0
2018-09-14T19:02:23.600
48409
2018-10-11T13:09:33.790
2018-09-15T01:13:22.577
2808
2808
question
curl url
curlでURLに #/<ファイル名> を付与してファイル命名できる挙動の正当性


せっかく私のコメントに関して追記していただいているので、無理にでも回答を書いてみるとこんな感じ。

まず、URL中の`#`の部分ですが、

<https://github.com/curl/curl/releases/download/curl-7_61_1/curl-7.61.1.tar.bz2.asc#/custom-
named-file>

に含まれる`#/custom-named-file`は[Fragment](https://www.rfc-
editor.org/rfc/rfc3986#section-3.5)と言うもの(RFCの本文中では _fragment identifier_
と記されているが、構文規則には`fragment`とだけ書いてある)で、URIとしては構文上正しいものです。この例で言うとURIのPath部分が`curl-7.61.1.tar.bz2.asc`までになることはURIの仕様として規定されています。

(ちなみに上の_Fragment_のリンクの中身は`https://www.rfc-
editor.org/rfc/rfc3986#section-3.5`と言うもので、この場合、`https://www.rfc-
editor.org/rfc/rfc3986`で表される文書の中で`section-3.5`と言う識別子で表される特別の部分を表す書き方です。)

通常の使い方では`#/custom-named-
file`の部分はサーバ側には送られないか、サーバ側では無視され、受信したリソース(今の場合はHTMLテキスト)のどの部分かを特定するのにブラウザ側で使われます。

ただし上記のRFC自体はFragmentの詳細な意味までは定めていないので、クライアント側がFragment部分を送信するかどうか、サーバ側が受信したFragmentをどう解釈するかは、使用するクライアント・サーバによります。(この辺り詳細な規定があるのか調べきれていません。)

従って、 **使用するクライアント・サーバによっては、Fragmentの有無で取得されるリソースが変わってくる可能性があります**
。curlの場合には(少なくともHTTPであれば)Fragment部分は送信しないようなので、この心配はなさそうです。

ただし、Fragment(あるいは`#/`のような`/`付きのFragment)が、下記の** _the file part of the remote
file_ **を表すと言う規定は(HTTP以外を探しても)存在しないでしょう。

* * *

_**これは何か公の仕様に則った挙動なのでしょうか**_

コメントに書いたようにcurlの場合、「公の仕様」と言えるものは[manページ](https://curl.haxx.se/docs/manpage.html)くらいしかありません。

現在のcurlの`-O`オプションの記述はこうなっています。

> ### -O, --remote-name
>
> Write output to a local file named like the remote file we get. (Only  
>  the file part of the remote file is used, the path is cut off.)
>
> The file will be saved in the current working directory. If you want  
>  the file saved in a different directory, make sure you change the  
>  current working directory before invoking curl with this option.
>
> The remote file name to use for saving is extracted from the given  
>  URL, nothing else, and if it already exists it will be overwritten. If  
>  you want the server to be able to choose the file name refer to -J,  
>  --remote-header-name which can be used in addition to this option. If the
> server chooses a file name and that name already exists it will  
>  not be overwritten.
>
> There is no URL decoding done on the file name. If it has %20 or other  
>  URL encoded parts of the name, they will end up as-is as file name.
>
> You may use this option as many times as the number of URLs you have.

**_Write output to a local file named like the remote file we get. (Only the
file part of the remote file is used, the path is cut off.)_** / **_The remote
file name to use for saving is extracted from the given  
URL_** / **_There is no URL decoding done on the file name._**
という記述がありますが、これを「Fragmentにパスの最後っぽい内容があればそれをremote
fileとする」という風に解釈するのは、無理でしょう。(もちろん明確にそうしない、とも取れない。)

つまり「何か公の仕様に則った挙動」とは言えない、と言うことになると思います。

* * *

_**curlの独自機能なのでしょうか**_

これもコメントどおりですが、現在の挙動が意図されたものかどうかがわかるような記述は見つけられませんでした。(数分間公式サイトをうろうろしただけですが。)

と言うわけで、(意図して提供されている)「独自機能」と言うよりは、「現在の実装ではそうなる」と言うことだと思われます。

* * *

_**Web系開発者から見て、curlの今回の挙動は、当然そうなるだろうというものなのか、いやいやおかしいだろうというものなのか**_

すでに上に述べたことでわかると思いますが、「当然そうなるだろう」と期待する根拠は「現在の実装がそうなっている」こと以外にはありません。ただし、「いやいやおかしいだろう」と言うものかどうかは人によるだろうと思います。(世間に流通しているシェルスクリプトなんかには、いろんなコマンドの「現在の実装」に基づく詳細動作に依存しているものはいっぱいあるでしょう。)

* * *

結論としては、ご自身で書かれているように「(現在の)curlのurlパース仕様を利用したハック技」と言えるだろうと思います。(ただ、便利ツールであるcurlが、「こんなのバグじゃないの?」なんて指摘を受けても、そこの実装が変更される可能性は低いと思いますが。)

私的には、curlには、ちゃんと出力先を示す`-o`オプション(わかっているとは思いますが、小文字の方です)があるんだから、素性のわからない`-O`と`#/name`なんて書き方を使わなくてもいいんじゃないの?と言う感じです。


CC BY-SA 4.0
2018-09-15T02:50:48.553
48413
2018-09-15T02:50:48.553
2021-10-07T07:34:52.683
-1
13972
48409
answer


本題については回答が付いていますので補足について記載します:

> 本件に疑問を持った発端は、 PowerShell 5.1で WebRequest を利用する場合も上記curlと同じような挙動を示すのに対し、
> PowerShell Core 6.1.0 では そのようなURLを渡すとステータスコード400エラー(Bad
> Request)になるのを見つけたことからです

PowerShell 5.1(5.1.17134.228)で fragment付きURLを指定した場合

[code]

    Invoke-WebRequest http://localhost/hello#fragment
    
[/code]

はfragmentが取り除かれた状態でGETしています(この挙動は、ウェブブラウザやcurlと同等なようです)。  
一方、PowerShell Core 6.1.0 では、入力された文字列そのまま、fragment付きでGETします。

少なくとも、Apache httpdではシャープを含むパスが指定された場合には400(Bad
Request)を返しているようで、結果として記載しているような応答の差異になって表れているようです。

PowerShellにおけるこの挙動の差異が仕様変更によるものなのかは未調査です(バグの可能性もあります)。

* * *

(追記)  
上に記載した挙動については不具合であるとして次で修正されました(.NET Coreに対する修正です。おそらく.NET Core
3.0で取り込まれるのではないでしょうか):

  * [HttpClient sending fragment portion of uri on request · Issue #32378 · dotnet/corefx](https://github.com/dotnet/corefx/issues/32378)

このコメントの中で、根拠として[RFC7230 Sec5.1 Identifying a Target Resource](https://www.rfc-
editor.org/rfc/rfc7230#section-5.1)が[挙げられていました](https://github.com/dotnet/corefx/issues/32378#issuecomment-424394055)。  
あとは、[Sec5.3](https://www.rfc-
editor.org/rfc/rfc7230#section-5.3)(特にSec5.3.1)も合わせて見ると分かりやすいのかな、と思いました。ここでは一切fragmentが登場しません。

OOPerさんの回答では

>
> 上記のRFC自体はFragmentの詳細な意味までは定めていないので、クライアント側がFragment部分を送信するかどうか、サーバ側が受信したFragmentをどう解釈するかは、使用するクライアント・サーバによります。(この辺り詳細な規定があるのか調べきれていません。)

と書かれていらっしゃいますが、このRFC7230を見た感じでは、サーバにはfragmentを投げてはいけない、が正解なのかな、と思われました。

また、サーバ側の話としては、上で

> Apache httpdではシャープを含むパスが指定された場合には400(Bad Request)を返している

と書きましたが、[こちらのコメント](https://github.com/dotnet/corefx/issues/32378#issuecomment-424489984)では404を返す、と書かれており、fragmentを受け取った場合の挙動はサーバ実装依存となっているようです。


CC BY-SA 4.0
2018-09-16T07:56:12.673
48451
2018-10-11T13:09:33.790
2021-10-07T07:34:52.683
-1
2808
48409
answer