📝

Windows上のcurlでftpサーバーから日本語名のファイルをダウンロードする

2024/06/23に公開

Zenn初投稿です。まだまだ未熟者ですがよろしくお願いいたします。

Git for Windows (Git BASH) 付属のcurlでftpサーバーからファイルをダウンロードしようとしたのですが、日本語名のファイルだと「ファイルが見つかりません」と言われてしまいました。
四苦八苦した結果、いくつか対処法を見つけたので記事にしてみました。

curl: (78) The file does not exist

curlでftpサーバーからファイルをダウンロードするため、下記のようなスクリプトを書きました。

test_1.sh
#!/bin/bash

set -eu

source './.env'
curl --show-error --silent \
    --ftp-method 'nocwd' --ssl-reqd --insecure \
    --remote-name \
    --user "${USERNAME}:${PASSWORD}" \
    'ftp://ftp.example.com/data/テスト.txt'

Git for Windowsインストール済みの環境でGit BASHからスクリプトを実行したところ、下記のエラーが出てダウンロードができませんでした。

curl: (78) The file does not exist

もちろん、ファイルはサーバー上に存在しています。
Ubuntu上で同じスクリプトを実行したところ、問題なくファイルをダウンロードできました。

原因

curlの公式リポジトリで回答がされていました。
どうやらWindows版のcurlでは必ずしもUnicodeがサポートされているとは限らないようです。

Unicodeがサポートされているかどうかは、バージョン情報のFeatures:で始まる行の中にUnicodeの記述があるかどうかで確認できます。
上記test_1.shを実行したGit for Windowsとcurlのバージョンは下記の通りです。

$ git --version
git version 2.45.2.windows.1

$ curl --version
curl 8.8.0 (x86_64-w64-mingw32) libcurl/8.8.0 Schannel zlib/1.3.1 brotli/1.1.0 zstd/1.5.6 libidn2/2.3.7 libpsl/0.21.5 libssh2/1.11.0
Release-Date: 2024-05-22
Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns ldap ldaps mqtt pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS brotli HSTS HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL SSPI threadsafe UnixSockets zstd

Features:で始まる行の中にUnicodeの記述がないため、Git for Windows付属のcurlではUnicodeがサポートされていないことがわかりました。

対処法

Windows付属のcurl.exeを利用する

実は最近のWindowsにはcurlがプリインストールされています。
PowerShellを立ち上げて確認したcurlのバージョンは下記の通りです。

PS C:\> curl.exe --version
curl 8.4.0 (Windows) libcurl/8.4.0 Schannel WinIDN
Release-Date: 2023-10-11
Protocols: dict file ftp ftps http https imap imaps pop3 pop3s smtp smtps telnet tftp
Features: AsynchDNS HSTS HTTPS-proxy IDN IPv6 Kerberos Largefile NTLM SPNEGO SSL SSPI threadsafe Unicode UnixSockets
curl.exeの.exeは省略できません。

.exeを省略して実行すると、下記の通りエラーが出てしまいます。
PowerShellではcurlのエイリアスにcurl.exeではなくInvoke-WebRequestというコマンドレットが設定されているためです。

PS C:\> curl --version
curl : リモート名を解決できませんでした。: '--version'
発生場所 行:1 文字:1
+ curl --version
+ ~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest]、WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

Features:で始まる行の中にUnicodeの記述があるため、このcurlを使えば良さそうです。
下記のようなスクリプトを書いてPowerShellから実行したところ、無事に日本語名のファイルをダウンロードできました。

test_1.ps1
# .env の読み込み
Get-Content -LiteralPath '.\.env' | Where-Object -FilterScript {
    $_ -ne '' -and $_ -notlike '#*'
} | ForEach-Object -Process {
    $name, $value = $_ -split '=', 2
    Set-Variable -Name $name -Value $value
}

curl.exe --show-error --silent `
    --ftp-method 'nocwd' --ssl-reqd --insecure `
    --remote-name `
    --user "${USERNAME}:${PASSWORD}" `
    'ftp://ftp.example.com/data/テスト.txt'

-s, --silentの代わりに-v, --verboseを付けて実行すると出力の日本語部分が文字化けしますが、ファイルのダウンロードは問題なくできるため深追いしないことにしました。

-K, --configオプションを利用する

Windows用にわざわざPowerShellスクリプトを用意したくない、どうしてもGit for Windows付属のcurlを使いたい、などの場合は-K, --configオプションを利用する方法もあります。

curlのコマンドライン引数をテキストファイルに記述し、そのファイルをcurlの-K, --configオプションで指定することで、日本語名のファイルでも正しく認識してくれるようになります。
下記のようなスクリプトを書いてGit BASHから実行したところ、無事に日本語名のファイルをダウンロードできました。

test_2.sh
#!/bin/bash

set -eu

source './.env'
curl --config './config_test_2.txt' \
    --user "${USERNAME}:${PASSWORD}"
config_test_2.txt
show-error
silent
ftp-method = "nocwd"
ssl-reqd
insecure
remote-name
url = "ftp://ftp.example.com/data/テスト.txt"

ただし、この方法には致命的な欠陥があります。
lsでダウンロードしたファイルを確認してみましょう。

$ ls
config_test_2.txt  test_2.sh*  繝・せ繝・txt

ファイル名が文字化けしてしまいます。
Git BASH (mintty) の設定を変更する、chcp.comでコードページを変更する、などいろいろ試しましたが、この文字化けを解消することはできませんでした。

仕方がないので、-O, --remote-nameの代わりに-o, --outputでダウンロード後のファイル名を指定します。

config_test_2.txt
@@ -5,3 +5,3 @@
 insecure
-remote-name
+output = "test_output.txt"
 url = "ftp://ftp.example.com/data/テスト.txt"

test_2.sh, lsを再実行し、ファイル名が指定した通りになっていることを確認します。

$ ls
config_test_2.txt  test_2.sh*  test_output.txt

WSL2をインストールする

WSL2は利用したことがないのですが、おそらくこれが最善な対処法でしょう。
機会があったらぜひWSL2にも触れてみたいです。

Ubuntu上での実行結果

Ubuntu上では、上記test_1.sh, test_2.shともに文字化けすることなく日本語名のファイルをダウンロードできました。
利用したcurlのバージョンは下記の通りです。

$ curl --version
curl 7.68.0 (x86_64-pc-linux-gnu) libcurl/7.68.0 OpenSSL/1.1.1f zlib/1.2.11 brotli/1.0.7 libidn2/2.2.0 libpsl/0.21.0 (+libidn2/2.2.0) libssh/0.9.3/openssl/zlib nghttp2/1.40.0 librtmp/2.3
Release-Date: 2020-01-08
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: AsynchDNS brotli GSS-API HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM NTLM_WB PSL SPNEGO SSL TLS-SRP UnixSockets

UnicodeはWindows版限定のfeatureなので、Ubuntu版curlのバージョン情報には記載がありません。

まとめ

この記事では、Windows上のcurlでftpサーバーから日本語名のファイルをダウンロードする方法を紹介しました。
Windowsのみの対応でよければ、Windows付属のcurl.exeを利用して、バッチファイルかPowerShellのスクリプトを書くことで対処できます。
どうしてもGit for Windows付属のcurlを使いたい場合は、-K, --configオプションを利用する方法もありますが、ダウンロードしたファイルの名前が文字化けするという問題があります。
可能であれば、WSL2をインストールして、Windows上でもLinux版curlを利用することが最善な対処法でしょう。

Discussion