团队里总有一堆零散的 shell 脚本:start.sh、build.sh、deploy.sh 散落在各处,新人看不懂、老项目不敢动。just 命令运行器就是为解决这个痛点而生的现代化任务编排工具——它像 Make 一样用 justfile 声明任务,但语法更贴合今天的开发习惯,跨平台、零依赖、自带参数与变量。本文带你从安装到实战,把项目里那些杂乱的脚本统一收编进一个可读、可维护的 justfile。你会发现,所谓「项目该会的操作」本就不该散落在十几个命名随意的脚本里。
一、为什么需要命令运行器
当项目变大,README 里的「怎么启动」「怎么打包」往往变成一串复制粘贴的命令。脚本一旦分叉,就会出现 dev.sh 和 dev2.sh 并存、参数写死、Windows 同事跑不起来的尴尬。更隐蔽的代价是:新同事入职第一天,往往要花半天追问「本地到底该怎么跑起来」,而这些知识只存在于老员工的脑子里。命令运行器(command runner)的价值,是把这些「项目该会的操作」沉淀成一份有版本、有依赖、可自解释的文件。Make 能做一部分,但它在跨平台和语法友好度上有明显短板,而 just 正是冲着这些短板来的。它不试图成为构建系统,只专注把「人要经常敲的命令」管好。
二、just 是什么
just 是一个用 Rust 写的任务运行器,核心就是一个 justfile:声明式地写出一个个「配方(recipe)」,然后在终端敲 just <任务名> 执行。相比 Make,它不需要你纠结制表符、不强制你的任务必须对应某个文件、还能在 Windows 上原生跑 PowerShell。它的设计哲学是「小而可预测」:没有隐含的文件目标规则,没有自动推导,你写什么它就跑什么。这种克制反而让它比 Make 更适合做「项目命令收纳箱」,新人读 justfile 就像读一份操作清单,不需要懂 Make 的那套隐式语义。
三、安装 just
3.1 各平台一行装好
它提供单文件二进制,几乎零依赖:
# macOS / Linux (Homebrew)
brew install just
# 跨平台 (Rust 工具链)
cargo install just
# Python 环境也能装,适合 CI 里临时用
pipx install rust-just
# Windows (Scoop)
scoop install just
装完敲 just --version 确认。注意 pipx install rust-just 装出来的命令名也是 just,在 GitHub Actions 里特别顺手——这点和我们讲 GitHub Actions 流水线 时「环境即代码」的思路一致。
四、第一个 justfile
在项目根目录新建 justfile(没有后缀),写下你的第一个任务:
# 默认任务:列出所有可用任务
default:
@just --list
# 启动开发服务器
dev:
npm run dev
# 运行测试
test:
pytest -q
行首 # 是任务说明,会显示在 just --list 里;@ 前缀表示「执行但不回显这条命令本身」,输出更干净。敲 just 或 just dev 即可运行。
五、核心特性
5.1 变量与字符串插值
app_name := "myapi"
port := "8080"
run:
python -m uvicorn {{app_name}}:app --port {{port}}
5.2 任务依赖
像 Make 一样用「任务名」声明依赖,被依赖的任务会先跑:
build:
npm run build
# deploy 之前先 build
deploy: build
./scripts/publish.sh
5.3 带参数的任务
# 部署到指定环境,默认 staging
deploy env="staging":
./scripts/deploy.sh {{env}}
调用:just deploy production。参数还能设类型,比如 count:int,避免误传字符串。更贴心的是,写在任务上方的 # 注释会自动出现在 just --list 的帮助里,相当于给每个任务配了内置文档。这也倒逼你把「这个任务到底干嘛、该传什么」写清楚,长期看是笔划算的维护账。
六、just 与 Make 的关键差异
| 维度 | just | make |
| 语法 | 类 Python,直观 | 制表符敏感,易踩坑 |
| 跨平台 | 原生支持 Windows/PowerShell | 需 MinGW / Cygwin |
| 变量 | 一等公民,支持插值 | 依赖 shell 变量 |
| 任务清单 | just --list 自带 | 需自行维护 |
| 依赖声明 | 显式、不易漏 | .PHONY 常漏写 |
七、实战:把项目脚本迁移到 just
下面是一个 Python 后端项目的真实 justfile 范例,把安装、检查、测试、打包、启动全部收编:
set shell := ["bash", "-uc"]
# 安装依赖
install:
pip install -r requirements.txt
# 代码检查(ruff + black)
lint:
ruff check .
black --check .
# 运行测试,可传具体用例名
test name="":
pytest -q {{name}}
# 构建并打包(先 lint 再 test)
package: lint test
python -m build
# 本地热重载启动
serve:
uvicorn app:app --reload
set shell := ["bash", "-uc"] 强制用 bash 且遇错即停,比默认 sh 更安全。搭配我们介绍过的 asdf 多语言版本管理,团队成员的环境与任务定义都能一致复现。把这个 justfile 提交进版本库后,它就和代码一样有了评审历史,「怎么发布」不再是个人口中的秘密,而是团队共享、可回滚的契约。新人 clone 下来第一条 just --list 就能看懂项目能做什么,省掉大量口头交接。
八、进阶技巧
8.1 跨平台脚本
用 #[windows] 标记 Windows 专属配方,just 会自动按平台选执行体:
#[linux]
open:
xdg-open dist/index.html
#[windows]
open:
start dist/index.html
8.2 与 CI 集成
在 GitHub Actions 里,用 rust-just 装好命令,直接复用本地同一套任务,避免「本地能跑、CI 跑不了」:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install rust-just
- run: just lint
- run: just test
这样本地和流水线的「检查口径」完全相同,也呼应了 后端命令行工具清单 里「把可复用操作固化成命令」的工程习惯。日常在编辑器里配合 VS Code 的任务面板,也能一键调用 just 任务。
九、排雷清单
1)忘记 @ 前缀:命令会被回显,输出变脏,CI 日志也难读。
2)变量未设默认值:带参任务不传参会报错,记得写 name="" 这类兜底。
3)Windows 行尾:justfile 用 LF,混用 CRLF 可能解析异常,提交时统一换行符。
4)依赖顺序误解:deploy: build 表示 deploy 依赖 build,不是「deploy 之后跑 build」。
十、模块拆分:大型项目怎么组织 justfile
项目一膨胀,单个 justfile 也会变长。这时可以用 import 把任务拆到多个文件,主文件负责汇总:
# 主 justfile
import "./tasks/db.just"
import "./tasks/deploy.just"
# 根任务仍可调用被导入文件里的任务
default:
@just --list
被导入的文件里同样写正常配方,调用时和本地任务没有区别。还有两个实用内建变量:justfile() 返回当前 justfile 的绝对路径,os() 返回当前系统(linux/macos/windows),配合它们能在任务里定位资源目录、写跨平台路径。当仓库按模块划分、每个模块有自己的运维操作时,这种拆分能让「谁的活谁维护」落到实处,而不是把所有命令堆在一个巨型文件里。
十一、总结
just 不替代 Make 的全部能力,但它精准命中了「项目日常命令收纳」这个高频场景:语法清爽、跨平台、自带清单与参数,还能用 import 平滑扩展。把散落的脚本收进 justfile,新人一条 just --list 就能看懂项目能做什么,本地与 CI 共用一套口径,维护成本直线下降。如果你的仓库里还躺着一堆命名随意的 *.sh,今天就用 just 把它们收编吧——一份可读的任务清单,胜过十次口头交接。




