Skip to content
shellmap

ftpTransfer files over FTP — legacy plaintext protocol; prefer SFTP today across all 5 shells

Equivalents in every shell

Bashunix
ftp ftp.example.com

Connects to a remote FTP server (default port 21). Authentication credentials and all data travel UNENCRYPTED — modern alternatives are `sftp` (SSH-based) and `https://...` for download-only. Linux `ftp` is typically `inetutils-ftp` or `ftp` from netkit. For scripted one-shot transfers, `curl ftp://...` is the more flexible binary.

Zshunix
ftp ftp.example.com

Same external. macOS REMOVED the system FTP client in 10.13 (High Sierra) — install via `brew install inetutils` for `gftp`, or use `curl ftp://...` (which honours `~/.netrc` for credentials and supports passive mode by default).

Fishunix
ftp ftp.example.com

Same external. Scripted FTP from fish is much cleaner via `curl` than the interactive client — fish's quoting rules and the FTP client's command-mode prompt mix awkwardly.

PowerShellwindows
$w = [System.Net.WebClient]::new(); $w.Credentials = [System.Net.NetworkCredential]::new('user','pass'); $w.DownloadFile('ftp://ftp.example.com/file','file')

PowerShell has NO `Invoke-FTPRequest` cmdlet. Standard options: (1) `.NET` `WebClient` / `FtpWebRequest` classes for full control, (2) the bundled Windows `ftp.exe`, (3) `Invoke-WebRequest` for basic `ftp://` GET/PUT (no listings). For real SFTP use the `Posh-SSH` module — modern, but not FTP.

cmd.exewindows
ftp -s:commands.txt

Built-in Windows FTP client. Interactive when launched bare; `-s:scriptfile` runs a sequence of FTP commands non-interactively. Limited (no passive-mode toggle on some builds), unencrypted, and reads the password from the script in cleartext — never check that file into git.

Worked examples

Download one file from an FTP server (one-shot)

Bash
curl -u user:pass ftp://ftp.example.com/path/file -o file
PowerShell
Invoke-WebRequest ftp://user:[email protected]/path/file -OutFile file
cmd.exe
ftp -s:get.txt

Upload a file to an FTP server

Bash
curl -T file -u user:pass ftp://ftp.example.com/incoming/
PowerShell
$c = [System.Net.WebClient]::new(); $c.Credentials = [System.Net.NetworkCredential]::new('user','pass'); $c.UploadFile('ftp://ftp.example.com/incoming/file','file')

List a remote directory

Bash
curl -u user:pass ftp://ftp.example.com/incoming/
PowerShell
((Invoke-WebRequest ftp://user:[email protected]/incoming/ -UseBasicParsing).Content -split '\r?\n')

Gotchas

  • Plaintext FTP transmits PASSWORDS and DATA unencrypted — anyone on the network sees everything. The replacement is `sftp` (SSH-based, port 22) for new work, or FTPS (FTP-over-TLS, `AUTH TLS` on port 21 / implicit on 990) for legacy servers that must stay on the FTP family. Don't deploy new FTP servers in 2026.
  • PASV vs PORT (passive vs active) mode decides which side opens the data channel. Behind NAT / firewalls, passive mode (`ftp -p`, `curl --ftp-pasv`) is almost always required. Some Windows `ftp.exe` builds DON'T support passive at all — the symptom is `LIST` / `GET` hanging after auth succeeds. Switch to `curl` or `Invoke-WebRequest` in that case.
  • macOS removed the system FTP client in 10.13. `brew install inetutils` provides `gftp`, but for most uses `curl ftp://...` is the right answer — one binary handles `.netrc` auth, passive mode, resume, and retry. The same `curl` recipe runs on Linux, macOS, and Windows (the bundled `curl.exe`).
  • PowerShell 5.1's `Invoke-WebRequest -Uri ftp://...` works for GET and accepts `-Credential`, but it ALWAYS buffers the whole response in memory before writing `-OutFile` — multi-GB transfers OOM. For large files use `[System.Net.FtpWebRequest]` with a streaming read, or shell out to `curl.exe`.
  • cmd `ftp -s:commands.txt` reads a script of FTP-level commands (`open host`, `user name`, `get file`, `bye`). The password line goes ON DISK in cleartext — never check it into git, and overwrite the file after use. The cleaner pattern is `curl` / `Invoke-WebRequest` with credentials passed via env or SecureString.

WSL & PowerShell Core notes

pwsh`Invoke-WebRequest` understands `ftp://` URIs on every pwsh platform, but it's NOT a full FTP client — only basic GET/PUT, no directory listing parser, no passive-mode tuning. For scripted FTP work that runs identically on Windows, Linux, and macOS pwsh, the canonical approach is `curl.exe ftp://...` (or `curl` on Unix) — same flags, same `.netrc`, same exit codes.
WSLWSL distros install `ftp` via `apt install ftp` (or just use `curl ftp://...`). Running `wsl ftp host` from PowerShell goes through interop and works exactly like Linux — useful when the Windows `ftp.exe` lacks passive-mode support, or when you want `.netrc` credential storage. Files transfer to the WSL filesystem under `/home/...`.

Related commands