A multi-cloud-disk command-line tool written in Go. Currently only Quark pan is supported; baidu / aliyun / onedrive / google-drive / lanzou / tianyi are planned via the provider interface.
Project site: https://phpgao.github.io/diskcli/
- Multi-provider architecture —
provider.Providerinterface abstracts the cloud disk; adding a new one only requires implementing the interface. - File & folder upload/download — chunked concurrent upload (configurable threads), resumable transfer, streaming download.
- Conflict policy —
skip/overwrite/renameon name collision. - Directory ops —
ls/mkdir/mv/cp/rm/rename. - Search — full-text search by file name across the drive or within a directory.
- Batch rename — literal / regex / case transforms with dry-run preview and undoable confirmation.
- Undoable rename —
rename --undorecords the change to a temp file and prompts for keep/rollback. - Share management — save, create, list, delete shares.
- kubectl-style output —
-o json|yaml|table|wide. - Multiple cookie sources —
--qk-cookieflag >QK_COOKIEenv >~/.diskcli/config. - Custom browser headers —
--qk-user-agent/--qk-referer/--qk-originflags,QK_USER_AGENT/QK_REFERER/QK_ORIGINenv vars, orqk_user_agent/qk_referer/qk_originconfig keys. - Structured logging —
-venablesslogdebug logs on stderr. - Decoupled protocol & tool —
Doerinterface abstracts HTTP for unit tests.
go install github.com/phpgao/diskcli/cmd/diskcli@latestBuild from source:
git clone <repo>
cd diskcli
go build -o diskcli ./cmd/diskcliConfig file: ~/.diskcli/config (TOML format).
--qk-cookieCLI flag — always wins, bypasses account selectionQK_COOKIEenv var — overrides the active account's cookie- Config file —
[account.<name>]section selected byDISK_ACCOUNTenv, or top-level keys whenDISK_ACCOUNTis unset
# Top-level keys = [default] account
qk_cookie = "__pus=xxx; __puus=yyy"
upload_threads = 4
# Optional custom browser headers
qk_user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36"
# Named accounts selected via DISK_ACCOUNT env var
[account.work]
qk_cookie = "work-cookie-value"
[account.personal]
qk_cookie = "personal-cookie-value"# Use default account
diskcli info
# Switch to [account.work]
DISK_ACCOUNT=work diskcli info
# Override with --qk-cookie (ignores DISK_ACCOUNT)
diskcli --qk-cookie "xxx" share save "https://pan.quark.cn/s/abc" /dst/The Quark cookie must contain both
__pusand__puus; missing__puusbreaks upload/download callback auth.
## Usage
### User info
```bash
diskcli info # nickname / member type / capacity
diskcli -v info # with debug logs
diskcli info -o json # JSON output
diskcli info -o yaml # YAML output
diskcli ls / # list root (names only)
diskcli ls /backup -o wide # long table (type/size/time/fid/name)
diskcli ls / -o json # JSON (pipe-friendly)
diskcli ls / -o yaml # YAMLdiskcli tree # print the directory tree from root
diskcli tree /backup # from a given path
diskcli tree /backup -L 2 # max depth
diskcli tree /backup -d # directories only
diskcli tree /backup -h # show file sizes
diskcli tree /backup -C 10 # traverse 10 directories in parallel (default 5)
diskcli mkdir /backup/photos
diskcli mv /file.txt /backup/ # move into existing dir
diskcli mv /file.txt /backup/new.txt # rename via move (same dir)
diskcli cp /file.txt /backup/ # copy into existing dir
diskcli cp /file.txt /backup/copy.txt # copy with rename (same dir)
diskcli rm /old.txt -y # skip confirm
diskcli rename /old.txt new.txtdiskcli search "report" # whole drive
diskcli search "invoice" /work # within a directory
diskcli search "backup" / -o json # JSON output# rename and record to a temp file; confirm or rollback
diskcli rename /file.txt new.txt --undo
# → renamed: /file.txt -> /new.txt
# → confirm to keep? (y=keep, n=rollback)
# y / n
# auto-confirm (keep)
diskcli rename /file.txt new.txt --undo -y# literal replace
diskcli brename /photos --match ".jpeg" --to ".jpg" -y
# regex with capture groups (single-quote --to to avoid shell expansion)
diskcli brename /photos --match 'photo_(\d+)' --to 'img_$1' --regex -y
# case transforms
diskcli brename /docs --upper -y
diskcli brename /docs --lower --dry-run
# undoable batch (rollback on reject)
diskcli brename /docs --match "draft" --to "final" --undoModes: --match+--to (literal), --regex (regex with $1 capture groups),
--upper, --lower. Flags: --dry-run (preview only), --undo (record +
confirm/rollback), -y (skip prompts).
# single file
diskcli upload ./file.txt /backup/file.txt
# upload into a directory (uses local filename)
diskcli upload ./file.txt /backup/
# folder (recursive)
diskcli upload ./folder /backup/folder --threads 8
# overwrite on conflict
diskcli upload ./file.txt /backup/ --on-conflict overwrite -y
# resumable
diskcli upload ./big.iso /backup/ --resumeFlags:
--threads— chunk concurrency (0 = config or server-suggested, default 3)--file-threads— per-file concurrency inside a folder (default 1, avoid rate limit)--on-conflict—skip(default) /overwrite/rename--yes/-y— skip confirm before overwrite--resume— resumable upload
diskcli download /backup/file.txt ./file.txt
diskcli download /backup/folder ./folder --resume
diskcli download /backup/file.txt ./ --on-conflict rename# save (transfer) a shared link to local dir
diskcli share save "https://pan.quark.cn/s/abc123 提取码:xxxx" /saved/
# create a share
diskcli share create /backup/file.txt
diskcli share create /file.txt --need-passcode --passcode 1234 --expire 3
# list my shares (default page 1, page size 50)
diskcli share list
# list with explicit pagination
diskcli share list --page 2 --page-size 100
# delete a share
diskcli share delete <share-id> -y
share listfetches one page at a time. The table prints the expiry as a timestamp; a permanent share is shown aspermanent. Use-o json/-o yamlfor the fullexpires_at/created_atfields.
internal/
├── provider/ generic cloud-disk interface (Provider/Sharer + shared types)
├── quark/ Quark protocol layer (Doer/CookieStore/5-step upload/share) + Adapter
├── transfer/ orchestration (conflict policy, folder recursion)
├── config/ cookie source merging
├── cli/ cobra command layer
└── pkg/logx/ slog wrapper
- Create
internal/<name>/and implement the protocol layer. - Create
internal/<name>/adapter.goimplementingprovider.Provider(andprovider.Sharerif the provider supports sharing). - Register it in
internal/cli/provider.gonewProvider.
The transfer and cli layers require no changes.
MIT