关于 justfile
用于运行项目专属命令的工具,命令以 recipe 形式定义在 `justfile` 中。开发者可通过命令行运行 recipe 及其依赖,并配置参数、环境和工作目录。
主要功能
- 01在 `justfile` 中定义项目命令
- 02recipe 参数与依赖
- 03recipe 列表和别名
- 04加载 `.env` 文件
- 05配置 Shell 与工作目录
- 06跨平台执行命令
适用场景
- 01运行项目测试和构建命令
- 02串联初始化或发布 recipe
- 03查看可用项目命令
- 04在不同操作系统间共享可复用命令
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ iloveitaly/ ai-skills --skill justfile安装后在 Claude Code 中输入/justfile确认已加载。
详细介绍
编写 Justfile
以下 README 摘自:https://github.com/casey/just/blob/master/README.md
本文档完整介绍了 Justfile 的语法和系统。
just
just 是一种便捷的方式,可用于保存和运行项目专属命令。
本文档也提供书籍版。书籍版反映最新发布版本,而 GitHub 上的 README 反映最新的 master 版本。
(中文文档在 这里, 快看过来!)
这些称为配方的命令保存在名为 justfile 的文件中,语法受
make 启发:

然后,你可以使用 just RECIPE 运行它们:
$ just test-all
cc *.c -o main
./test --all
Yay, all your tests passed!
just 有许多实用功能,相比 make 也有很多改进:
just是命令运行器,而非构建系统,因此避免了许多make的复杂性和特殊行为。 无需使用.PHONY配方!- 支持 Linux、MacOS、Windows 和其他常见 Unix 系统,无需额外依赖。(不过,如果你的系统没有
sh,就需要选择其他 shell。) - 错误信息明确且详尽,语法错误会附带其源代码上下文。
- 配方可以接受命令行参数。
- 尽可能在静态阶段解决错误。未知配方和循环依赖会在运行任何内容之前报告。
just会加载.env文件,方便设置环境变量。- 可以从命令行列出配方。
- 大多数常用 shell 都有可用的命令行补全脚本。
- 配方可以使用任意语言编写,例如 Python 或 NodeJS。
just可以从任何子目录调用,而不仅限于包含justfile的目录。- 还有更多功能!
如果你需要 just 的帮助,欢迎提交 issue 或在
Discord 上联系我。功能请求和错误报告
始终受欢迎!
快速开始
请参阅安装部分,了解如何在电脑上安装 just。尝试运行 just --version,确认
安装正确。
如需了解语法概览,请查看 这份速查表。
安装并正常运行 just 后,请在项目根目录创建名为 justfile 的文件,内容如下:
recipe-name:
echo 'This is a recipe!'
# this is a comment
another-recipe:
@echo 'This is another recipe.'
调用 just 时,它会在当前目录及其上级目录中查找文件 justfile,因此你可以从项目的任何子目录调用它。
查找 justfile 时不区分大小写,因此任何大小写形式都可以使用,例如 Justfile、
JUSTFILE 或 JuStFiLe。如果你想隐藏 justfile,just 也会查找名为
.justfile 的文件。
不带参数运行 just 会运行 justfile 中的第一个配方:
$ just
echo 'This is a recipe!'
This is a recipe!
指定一个或多个参数即可运行相应的配方:
$ just another-recipe
This is another recipe.
just 会在运行命令前将其打印到标准错误输出,这就是为什么
echo 'This is a recipe!' 会被打印出来。以 @ 开头的行不会打印命令,因此
echo 'This is another recipe.' 没有被打印出来。
如果命令失败,配方就会停止运行。此处只有在 cargo test 成功后才会运行
cargo publish:
publish:
cargo test
# tests passed, time to publish!
cargo publish
配方可以依赖其他配方。此处 test 配方依赖 build 配方,因此会先运行
build,再运行 test:
build:
cc main.c foo.c bar.c -o main
test: build
./test
sloc:
@echo "`wc -l *.c` lines of code"
$ just test
cc main.c foo.c bar.c -o main
./test
testing… all tests passed!
没有依赖项的配方会按照它们在命令行中给出的顺序运行:
$ just build sloc
cc main.c foo.c bar.c -o main
1337 lines of code
依赖项始终会先运行,即使它们在依赖它们的配方之后传入:
$ just test build
cc main.c foo.c bar.c -o main
./test
testing… all tests passed!
配方可以依赖子模块中的配方:
mod foo
baz: foo::bar
示例
各种 justfile 示例可在
examples 目录和
GitHub上找到。
功能
默认配方
调用 just 时如果没有指定配方,它会运行带有
[default] 属性的配方;如果没有配方带有 [default] 属性,则运行 justfile 中的第一个配方。
这个配方可以是项目中最常运行的命令,例如运行测试:
test:
cargo test
你也可以使用依赖项来默认运行多个配方:
default: lint build test
build:
echo Building…
test:
echo Testing…
lint:
echo Linting…
如果没有适合作为默认配方的配方,你可以在 justfile 开头添加一个列出可用配方的配方:
default:
just --list
列出可用配方
使用 just --list 可以按字母顺序列出配方:
$ just --list
Available recipes:
build
test
deploy
lint
使用 just --list PATH 可以列出子模块中的配方,其中
PATH 是以空格或 :: 分隔的模块路径:
$ cat justfile
mod foo
$ cat foo.just
mod bar
$ cat bar.just
baz:
$ just --list foo bar
Available recipes:
baz
$ just --list foo::bar
Available recipes:
baz
just --summary 更简洁:
$ just --summary
build test deploy lint
传入 --unsorted,可按配方在 justfile 中出现的顺序打印:
test:
echo 'Testing!'
build:
echo 'Building!'
$ just --list --unsorted
Available recipes:
test
build
$ just --summary --unsorted
test build
如果你希望 just 默认列出 justfile 中的配方,可以将以下内容用作默认配方:
default:
@just --list
请注意,你可能需要在上面的行中添加 --justfile {{justfile()}}。
如果没有这个参数,当你执行 just -f /some/distant/justfile -d . 或
just -f ./non-standard-justfile 时,配方中的 just --list 不一定会使用你指定的文件。它会尝试在当前路径中查找 justfile,甚至可能导致 No justfile found 错误。
可以使用 --list-heading 自定义标题文本:
$ just --list --list-heading $'Cool stuff…\n'
Cool stuff…
test
build
也可以使用 --list-prefix 自定义缩进:
$ just --list --list-prefix ····
Available recipes:
····test
····build
--list-heading 的参数会替换标题和其后的换行符,因此只要参数非空,就应包含一个换行符。这样设计是为了让你可以传入空字符串,从而完全隐藏标题行:
$ just --list --list-heading ''
test
build
调用多个配方
可以在命令行中一次调用多个配方:
build:
make web
serve:
python3 -m http.server -d out 8000
$ just build serve
make web
python3 -m http.server -d out 8000
请注意,带参数的配方会吞掉参数,即使这些参数与其他配方的名称相同:
build project:
make {{project}}
serve:
python3 -m http.server -d out 8000
$ just build serve
make: *** No rule to make target `serve'. Stop.
可以使用 --one 标志,将命令行调用限制为单个配方:
$ just --one build serve
error: Expected 1 command-line recipe invocation but found 2.
工作目录
默认情况下,配方会在工作目录设为包含 justfile 的目录的情况下运行。
可以使用 [no-cd] 属性,让配方在工作目录设为调用 just 时所在目录的情况下运行。
@foo:
pwd
[no-cd]
@bar:
pwd
$ cd subdir
$ just foo
/
$ just bar
/subdir
可以使用 set working-directory := '…' 为所有配方指定工作目录:
set working-directory := 'bar'
@foo:
pwd
$ pwd
/home/bob
$ just foo
/home/bob/bar
可以使用 working-directory 属性1.38.0为特定配方指定工作目录:
[working-directory: 'bar']
@foo:
pwd
$ pwd
/home/bob
$ just foo
/home/bob/bar
working-directory 设置或 working-directory 属性的参数可以是绝对路径或相对路径。如果是相对路径,则相对于默认工作目录进行解释。
别名
别名允许在命令行中使用其他名称调用配方:
alias b := build
build:
echo 'Building!'
$ just b
echo 'Building!'
Building!
别名的目标可以是子模块中的配方:
mod foo
alias baz := foo::bar
设置
设置用于控制解释和执行方式。每项设置在 justfile 中最多只能指定一次,位置不限。
例如:
set shell := ["zsh", "-cu"]
foo:
# this line will be run as `zsh -cu 'ls **/*.txt'`
ls **/*.txt
设置表
| 名称 | 值 | 默认值 | 描述 |
|---|---|---|---|
allow-duplicate-recipes | boolean | false | 允许 justfile 中后出现的配方覆盖同名的先前配方。 |
allow-duplicate-variables | boolean | false | 允许 justfile 中后出现的变量覆盖同名的先前变量。 |
dotenv-filename | string | - | 如果存在,则加载使用自定义名称的 .env 文件。 |
dotenv-load | boolean | false | 如果存在,则加载 .env 文件。 |
dotenv-override | boolean | false | 使用 .env 文件中的值覆盖现有环境变量。 |
dotenv-path | string | - | 从自定义路径加载 .env 文件;如果文件不存在则报错。此设置会覆盖 dotenv-filename。 |
dotenv-required | boolean | false | 如果未找到 .env 文件则报错。 |
export | boolean | false | 将所有变量导出为环境变量。 |
fallback | boolean | false | 如果命令行上的第一个配方未找到,则在父目录中搜索 justfile。 |
ignore-comments | boolean | false | 忽略以 # 开头的配方行。 |
positional-arguments | boolean | false | 传递位置参数。 |
quiet | boolean | false | 执行前不回显配方行。 |
script-interpreter1.33.0 | [COMMAND, ARGS…] | ['sh', '-eu'] | 设置用于调用带有空 [script] 属性的配方的命令。 |
shell | [COMMAND, ARGS…] | - | 设置用于调用配方和计算反引号表达式的命令。 |
tempdir | string | - | 在 tempdir 中创建临时目录,而不是使用系统默认的临时目录。 |
unstable1.31.0 | boolean | false | 启用不稳定功能。 |
windows-powershell | boolean | false | 在 Windows 上使用 PowerShell 作为默认 shell。(已弃用。请改用 windows-shell。 |
windows-shell | [COMMAND, ARGS…] | - | 设置用于调用配方和计算反引号表达式的命令。 |
working-directory1.33.0 | string | - | 设置配方和反引号表达式的工作目录,相对于默认工作目录。 |
布尔设置可以这样写:
set NAME
这等同于:
set NAME := true
非布尔设置可以设为字符串或表达式。1.46.0
但是,由于设置会影响反引号表达式和许多函数的行为,这些表达式不能直接包含反引号或函数调用,也不能通过引用间接包含。
允许重复配方
如果将 allow-duplicate-recipes 设为 true,则定义多个同名配方不会报错,并且会使用最后一个定义。默认值为 false。
set allow-duplicate-recipes
@foo:
echo foo
@foo:
echo bar
$ just foo
bar
允许重复变量
如果将 allow-duplicate-variables 设为 true,则定义多个同名变量不会报错,并且会使用最后一个定义。默认值为 false。
set allow-duplicate-variables
a := "foo"
a := "bar"
@foo:
echo {{a}}
$ just foo
bar
Dotenv 设置
如果设置了 dotenv-load、dotenv-filename、dotenv-override、dotenv-path 或
dotenv-required 中的任意一项,just 就会尝试从文件中加载环境变量。
如果设置了 dotenv-path,just 会在给定路径查找文件,该路径可以是绝对路径,也可以是相对于工作目录的路径。
命令行选项 --dotenv-path(简写形式为 -E)可用于在运行时设置或覆盖 dotenv-path。
如果设置了 dotenv-filename,just 会在相对于工作目录及其每个父目录的给定路径查找文件。
如果未设置 dotenv-filename,但设置了 dotenv-load 或 dotenv-required,just 会在相对于工作目录及其每个父目录的位置查找名为 .env 的文件。
dotenv-filename 和 dotenv-path 类似,但 dotenv-path 只会相对于工作目录检查,而 dotenv-filename 会相对于工作目录及其每个父目录检查。
除非设置了 dotenv-required,否则未找到环境文件不会报错。
加载的变量是环境变量,而不是 just 变量,因此必须在配方和反引号中使用 $VARIABLE_NAME 访问。
如果设置了 dotenv-override,环境文件中的变量将覆盖现有环境变量。
例如,如果你的 .env 文件包含:
# a comment, will be ignored
DATABASE_ADDRESS=localhost:6379
SERVER_PORT=1337
而你的 justfile 包含:
set dotenv-load
serve:
@echo "Starting server with database $DATABASE_ADDRESS on port $SERVER_PORT…"
./server --database $DATABASE_ADDRESS --port $SERVER_PORT
just serve 将输出:
$ just serve
Starting server with database localhost:6379 on port 1337…
./server --database $DATABASE_ADDRESS --port $SERVER_PORT
导出
export 设置会将所有 just 变量导出为环境变量。默认值为 false。
set export
a := "hello"
@foo b:
echo $a
echo $b
$ just foo goodbye
hello
goodbye
位置参数
如果 positional-arguments 为 true,配方参数将作为位置参数传递给命令。对于逐行配方,参数 $0 将是配方名称。
例如,运行此配方:
set positional-arguments
@foo bar:
echo $0
echo $1
将产生以下输出:
$ just foo hello
foo
hello
使用 bash 或 zsh 等兼容 sh 的 shell 时,$@ 会展开为传递给配方的位置参数,从第一个参数开始。在双引号中用作 "$@" 时,包含空白字符的参数会像用双引号括起来一样传递。也就是说,"$@" 等同于 "$1" "$2"……
没有位置参数时,"$@" 和 $@ 会展开为空(即被移除)。
此示例配方会将参数逐个打印到单独的行中:
set positional-arguments
@test *args='':
bash -c 'while (( "$#" )); do echo - $1; shift; done' -- "$@"
使用 两个 参数运行:
$ just test foo "bar baz"
- foo
- bar baz
也可以通过 [positional-arguments] 属性1.29.0,按配方单独启用位置参数:
[positional-arguments]
@foo bar:
echo $0
echo $1
请注意,PowerShell 处理位置参数的方式与其他 shell 不同,因此启用位置参数很可能会导致使用 PowerShell 的配方无法正常工作。
如果使用 PowerShell 7.4 或更高版本,-CommandWithArgs 标志可使位置参数按预期工作:
set shell := ['pwsh.exe', '-CommandWithArgs']
set positional-arguments
print-args a b c:
Write-Output @($args[1..($args.Count - 1)])
Shell
shell 设置控制用于调用配方行和反引号的命令。Shebang 配方不受影响。默认 shell 为 sh -cu。
# use python3 to execute recipe lines and backticks
set shell := ["python3", "-c"]
# use print to capture result of evaluation
foos := `print("foo" * 4)`
foo:
print("Snake snake snake snake.")
print("{{foos}}")
just 会将要执行的命令作为参数传递。许多 shell 需要额外的标志(通常是 -c),才能对第一个参数求值。
Windows Shell
在 Windows 上,just 默认使用 sh。要在 Windows 上使用其他 shell,请使用 windows-shell:
set windows-shell := ["powershell.exe", "-NoLogo", "-Command"]
hello:
Write-Host "Hello, world!"
请参阅 powershell.just,查看一个在所有平台上使用 PowerShell 的 justfile。
Windows PowerShell
set windows-powershell 使用旧版 powershell.exe 二进制文件,现已不再推荐。有关更灵活地控制 Windows 上所用 shell 的方式,请参阅上面的 windows-shell 设置。
在 Windows 上,just 默认使用 sh。若要改用 powershell.exe,请将 windows-powershell 设为 true。
set windows-powershell := true
hello:
Write-Host "Hello, world!"
Python 3
set shell := ["python3", "-c"]
Bash
set shell := ["bash", "-uc"]
Z Shell
set shell := ["zsh", "-uc"]
Fish
set shell := ["fish", "-c"]
Nushell
set shell := ["nu", "-c"]
如果要将默认表格模式更改为 light:
set shell := ['nu', '-m', 'light', '-c']
Nushell 使用 Rust 编写,支持 Windows、macOS 和 Linux 跨平台运行。
文档注释
紧挨着配方前面的注释会显示在 just --list 中:
# build stuff
build:
./bin/build
# test stuff
test:
./bin/test
$ just --list
Available recipes:
build # build stuff
test # test stuff
可以使用 [doc] 属性设置或隐藏配方的文档注释:
# This comment won't appear
[doc('Build stuff')]
build:
./bin/build
# This one won't either
[doc]
test:
./bin/test
$ just --list
Available recipes:
build # Build stuff
test
表达式和替换
表达式支持多种运算符和函数调用,可用于赋值、配方参数默认值以及配方主体中的 {{…}} 替换。
tmpdir := `mktemp -d`
version := "0.2.7"
tardir := tmpdir / "awesomesauce-" + version
tarball := tardir + ".tar.gz"
config := quote(config_dir() / ".project-config")
publish:
rm -f {{tarball}}
mkdir {{tardir}}
cp README.md *.c {{ config }} {{tardir}}
tar zcvf {{tarball}} {{tardir}}
scp {{tarball}} [email protected]:release/
rm -rf {{tarball}} {{tardir}}
拼接
+ 运算符会将左侧参数与右侧参数拼接后返回:
foobar := 'foo' + 'bar'
逻辑运算符
逻辑运算符 && 和 || 可用于合并字符串值1.37.0,类似于 Python 中的 and 和 or。这些运算符将空字符串 '' 视为假,其他所有字符串都视为真。
这些运算符目前尚不稳定。
如果左侧参数为空字符串,&& 运算符会返回空字符串;否则会返回右侧参数:
foo := '' && 'goodbye' # ''
bar := 'hello' && 'goodbye' # 'goodbye'
如果左侧参数非空,|| 运算符会返回左侧参数;否则会返回右侧参数:
foo := '' || 'goodbye' # 'goodbye'
bar := 'hello' || 'goodbye' # 'hello'
拼接路径
/ 运算符可用于用斜杠拼接两个字符串:
foo := "a" / "b"
$ just --evaluate foo
a/b
请注意,即使其中一个字符串已经包含 /,仍会添加一个 /:
foo := "a/"
bar := foo / "b"
$ just --evaluate bar
a//b
也可以构造绝对路径1.5.0:
foo := / "b"
$ just --evaluate foo
/b
/ 运算符使用 / 字符,即使在 Windows 上也是如此。因此,对于使用通用命名约定(UNC)的路径,应避免使用 / 运算符;这类路径以 \? 开头,因为 UNC 路径不支持正斜杠。
转义 {{
要编写包含 {{ 的配方,请使用 {{{{:
braces:
echo 'I {{{{LOVE}} curly braces!'
(未匹配的 }} 会被忽略,因此无需转义。)
另一种方法是将所有要转义的文本放在插值中:
braces:
echo '{{'I {{LOVE}} curly braces!'}}'
还可以使用 {{ "{{" }}:
braces:
echo 'I {{ "{{" }}LOVE}} curly braces!'
字符串
支持使用 'single'、"double" 和 '''triple''' 引号的字符串字面量。与配方主体不同,字符串中不支持 {{…}} 插值。
双引号字符串支持转义序列:
carriage-return := "\r"
double-quote := "\""
newline := "\n"
no-newline := "\
"
slash := "\\"
tab := "\t"
unicode-codepoint := "\u{1F916}"
$ just --evaluate
"arriage-return := "
double-quote := """
newline := "
"
no-newline := ""
slash := "\"
tab := " "
unicode-codepoint := "🤖"
Unicode 字符转义序列 \u{…}1.36.0 最多可接受 6 个十六进制数字。
字符串可以包含换行符:
single := '
hello
'
double := "
goodbye
"
单引号字符串不识别转义序列:
escapes := '\t\n\r\"\\'
$ just --evaluate
escapes := "\t\n\r\"\\"
支持使用三重单引号或双引号括起来的缩进版本单引号和双引号字符串。缩进字符串行会去掉开头的换行符,以及所有非空行共有的前导空白:
# this string will evaluate to `foo\nbar\n`
x := '''
foo
bar
'''
# this string will evaluate to `abc\n wuv\nxyz\n`
y := """
abc
wuv
xyz
"""
与非缩进字符串类似,缩进的双引号字符串会处理转义序列,而缩进的单引号字符串会忽略转义序列。转义序列会在去除缩进后处理。去除缩进的算法不会将转义序列产生的空白或换行符纳入考虑。
Shell 扩展字符串
以 x 为前缀的字符串会进行 Shell 扩展1.27.0:
foobar := x'~/$FOO/${BAR}'
| 值 | 替换内容 |
|---|---|
$VAR | 环境变量 VAR 的值 |
${VAR} | 环境变量 VAR 的值 |
${VAR:-DEFAULT} | 环境变量 VAR 的值;如果 VAR 未设置,则使用 DEFAULT |
开头的 ~ | 当前用户的主目录路径 |
开头的 ~USER | USER 的主目录路径 |
此扩展会在编译时执行,因此无法使用 .env 文件中的变量和导出的 just 变量。不过,这样一来,Shell 扩展字符串便可用于设置和导入路径等场景,因为这些场景无法依赖 just 变量和 .env 文件。
格式字符串
以 f 为前缀的字符串是格式字符串1.44.0:
name := "world"
message := f'Hello, {{name}}!'
格式字符串可以包含由 {{…}} 界定的插值,其中包含表达式。格式字符串的计算结果是字符串片段与计算后的表达式拼接而成的字符串。
使用 {{{{ 在格式字符串中包含字面量 {{:
foo := f'I {{{{LOVE} curly braces!'
忽略错误
通常,如果命令返回非零退出状态,执行就会停止。若要在命令失败后仍继续执行,请在命令前加上 -:
foo:
-cat foo
echo 'Done!'
$ just foo
cat foo
cat: foo: No such file or directory
echo 'Done!'
Done!
函数
just 提供了许多可在表达式中使用的内置函数,包括配方正文中的 {{…}} 替换、赋值和默认参数值。
所有以 _directory 结尾的函数都可以缩写为 _dir。因此,home_directory() 也可以写作 home_dir()。此外,invocation_directory_native() 可以缩写为 invocation_dir_native()。
系统信息
arch()— 指令集架构。可能的值有:"aarch64"、"arm"、"asmjs"、"hexagon"、"mips"、"msp430"、"powerpc"、"powerpc64"、"s390x"、"sparc"、"wasm32"、"x86"、"x86_64"和"xcore"。num_cpus()1.15.0 - 逻辑 CPU 数量。os()— 操作系统。可能的值有:"android"、"bitrig"、"dragonfly"、"emscripten"、"freebsd"、"haiku"、"ios"、"linux"、"macos"、"netbsd"、"openbsd"、"solaris"和"windows"。os_family()— 操作系统家族;可能的值有:"unix"和"windows"。
例如:
system-info:
@echo "This is an {{arch()}} machine".
$ just system-info
This is an x86_64 machine
os_family() 函数可用于创建能在各种操作系统上运行的跨平台 justfile。示例请参阅 cross-platform.just 文件。
外部命令
-
shell(command, args...)1.27.0 返回 shell 脚本command的标准输出,并接受零个或多个位置参数args。用于解释command的 shell 与用于执行配方行的 shell 相同,可通过set shell := […]更改。command会作为第一个参数传入,因此,如果命令是'echo $@',在默认 shell 命令sh -cu下,参数为'foo'和'bar'时,完整命令行将是:'sh' '-cu' 'echo $@' 'echo $@' 'foo' 'bar'这样
$@就能按预期工作,而$1表示第一个参数。$@不包括第一个位置参数,因为该参数应是正在运行的程序名称。
# arguments can be variables or expressions
file := '/sys/class/power_supply/BAT0/status'
bat0stat := shell('cat $1', file)
# commands can be variables or expressions
command := 'wc -l'
output := shell(command + ' "$1"', 'main.c')
# arguments referenced by the shell command must be used
empty := shell('echo', 'foo')
full := shell('echo $1', 'foo')
error := shell('echo $1')
# Using python as the shell. Since `python -c` sets `sys.argv[0]` to `'-c'`,
# the first "real" positional argument will be `sys.argv[2]`.
set shell := ["python3", "-c"]
olleh := shell('import sys; print(sys.argv[2][::-1])', 'hello')
环境变量
env(key)1.15.0 — 获取名为key的环境变量;如果该变量不存在,则终止执行。
home_dir := env('HOME')
test:
echo "{{home_dir}}"
$ just
/home/user1
env(key, default)1.15.0 — 获取名为key的环境变量;如果该变量不存在,则返回default。env_var(key)—env(key)的已弃用别名。env_var_or_default(key, default)—env(key, default)的已弃用别名。
使用 || 运算符,可在环境变量值为空时替换为默认值;此功能目前尚不稳定:
set unstable
foo := env('FOO', '') || 'DEFAULT_VALUE'
可执行文件
-
require(name)1.39.0 — 在PATH环境变量中的目录里搜索可执行文件name,并返回其完整路径;如果不存在名为name的可执行文件,则中止并报错。bash := require("bash") @test: echo "bash: '{{bash}}'"$ just bash: '/bin/bash' -
which(name)1.39.0 — 在PATH环境变量中的目录里搜索可执行文件name,并返回其完整路径;如果不存在名为name的可执行文件,则返回空字符串。目前尚不稳定。set unstable bosh := which("bosh") @test: echo "bosh: '{{bosh}}'"$ just bosh: ''
调用信息
is_dependency()- 如果当前配方是作为另一配方的依赖项运行,而非直接运行,则返回字符串true;否则返回字符串false。
调用目录
invocation_directory()- 获取调用just时的当前目录绝对路径,即just在执行命令前更改目录(执行 chdir)之前的路径。在 Windows 上,invocation_directory()使用cygpath将调用目录转换为与 Cygwin 兼容的、以/分隔的路径。要在所有平台上返回原样的调用目录,请使用invocation_directory_native()。
例如,要对用户/调用者视角中的“当前目录”下的文件调用 rustfmt,请使用以下规则:
rustfmt:
find {{invocation_directory()}} -name \*.rs -exec rustfmt {} \;
或者,如果命令需要从当前目录运行,可以使用(例如):
build:
cd {{invocation_directory()}}; ./some_script_that_needs_to_be_run_from_here
invocation_directory_native()- 获取调用just时的当前目录绝对路径,即just在执行命令前更改目录(执行 chdir)之前的路径。
Justfile 和 Justfile 目录
justfile()- 获取当前justfile的路径。justfile_directory()- 获取当前justfile所在父目录的路径。
例如,要运行相对于当前 justfile 所在位置的命令:
script:
{{justfile_directory()}}/scripts/some_script
源文件和源目录
source_file()1.27.0 - 获取当前源文件的路径。source_directory()1.27.0 - 获取当前源文件所在父目录的路径。
在根 justfile 中,source_file() 和 source_directory() 的行为与 justfile() 和
justfile_directory() 相同;但在导入或子模块中调用时,分别返回当前 import 或 mod 源文件的路径和目录。
Just 可执行文件
just_executable()-just可执行文件的绝对路径。
例如:
executable:
@echo The executable is at: {{just_executable()}}
$ just
The executable is at: /bin/just
Just 进程 ID
just_pid()-just可执行文件的进程 ID。
例如:
pid:
@echo The process ID is: {{ just_pid() }}
$ just
The process ID is: 420
字符串操作
append(suffix, s)1.27.0 将suffix附加到s中以空白分隔的字符串后。append('/src', 'foo bar baz')→'foo/src bar/src baz/src'prepend(prefix, s)1.27.0 将prefix添加到s中以空白分隔的字符串前。prepend('src/', 'foo bar baz')→'src/foo src/bar src/baz'encode_uri_component(s)1.27.0 - 对s中除[A-Za-z0-9_.!~*'()-]以外的字符进行百分号编码,行为与 JavaScriptencodeURIComponent函数一致。quote(s)- 将所有单引号替换为'\'',并在s前后添加单引号。这足以为许多 Shell(包括大多数 Bourne Shell 派生版本)转义特殊字符。replace(s, from, to)- 将s中所有from替换为to。replace_regex(s, regex, replacement)- 将s中所有匹配regex的内容替换为replacement。正则表达式由 Rustregexcrate 提供。有关用法示例,请参阅语法文档。支持捕获组。replacement字符串使用替换字符串语法。trim(s)- 移除s开头和结尾的空白。trim_end(s)- 移除s末尾的空白。trim_end_match(s, substring)- 移除s中与substring匹配的后缀。trim_end_matches(s, substring)- 重复移除s中与substring匹配的后缀。trim_start(s)- 移除s开头的空白。trim_start_match(s, substring)- 移除s中与substring匹配的前缀。trim_start_matches(s, substring)- 重复移除s中与substring匹配的前缀。
大小写转换
capitalize(s)1.7.0 - 将s的首字符转换为大写,其余字符转换为小写。kebabcase(s)1.7.0 - 将s转换为kebab-case。lowercamelcase(s)1.7.0 - 将s转换为lowerCamelCase。lowercase(s)- 将s转换为小写。shoutykebabcase(s)1.7.0 - 将s转换为SHOUTY-KEBAB-CASE。shoutysnakecase(s)1.7.0 - 将s转换为SHOUTY_SNAKE_CASE。snakecase(s)1.7.0 - 将s转换为snake_case。titlecase(s)1.7.0 - 将s转换为Title Case。uppercamelcase(s)1.7.0 - 将s转换为UpperCamelCase。uppercase(s)- 将s转换为大写。
路径操作
可能失败
absolute_path(path)- 工作目录中相对路径path的绝对路径。在目录/foo中,absolute_path("./bar.txt")的结果是/foo/bar.txt。canonicalize(path)1.24.0 - 通过解析符号链接,并在可能的情况下移除.,..和多余的/,规范化path。extension(path)-path的扩展名。extension("/foo/bar.txt")的结果是txt。file_name(path)- 移除所有前置目录部分后的path文件名。file_name("/foo/bar.txt")的结果是bar.txt。file_stem(path)- 不含扩展名的path文件名。file_stem("/foo/bar.txt")的结果是bar。parent_directory(path)-path的父目录。parent_directory("/foo/bar.txt")的结果是/foo。without_extension(path)- 移除扩展名后的path。without_extension("/foo/bar.txt")的结果是/foo/bar。
这些函数可能会失败,例如路径没有扩展名时,这会导致执行中止。
不会失败
clean(path)- 通过移除多余的路径分隔符、中间的.部分,以及在可能的情况下移除..,简化path。clean("foo//bar")的结果是foo/bar,clean("foo/..")的结果是.,clean("foo/./bar")的结果是foo/bar。join(a, b…)- 此函数在 Unix 上使用/,在 Windows 上使用\,可能导致非预期行为。除非需要在 Windows 上使用\,否则应考虑使用/运算符(例如a / b)替代,该运算符始终使用/。 将路径a与路径b拼接。join("foo/bar", "baz")的结果是foo/bar/baz。接受两个或更多参数。
文件系统访问
path_exists(path)- 如果路径指向一个存在的实体,则返回true,否则返回false。会遍历符号链接;如果路径无法访问或指向失效的符号链接,则返回false。read(path)1.39.0 - 以字符串形式返回path处文件的内容。
错误报告
error(message)- 中止执行,并向用户报告错误message。
UUID 和哈希生成
blake3(string)1.25.0 - 以十六进制字符串形式返回string的 BLAKE3 哈希值。blake3_file(path)1.25.0 - 以十六进制字符串形式返回path处文件的 BLAKE3 哈希值。sha256(string)- 以十六进制字符串形式返回string的 SHA-256 哈希值。sha256_file(path)- 以十六进制字符串形式返回path处文件的 SHA-256 哈希值。uuid()- 生成随机的版本 4 UUID。
随机
choose(n, alphabet)1.27.0 - 从alphabet中随机选取字符,生成长度为n的字符串;字符可能不重复。例如,choose('64', HEX)将生成一个由小写十六进制字符组成的 64 字符随机字符串。
日期时间
datetime(format)1.30.0 - 按照format返回本地时间。datetime_utc(format)1.30.0 - 按照format返回 UTC 时间。
datetime 和 datetime_utc 的参数是 strftime 风格的格式字符串。详情请参阅
chrono 库文档。
语义化版本
semver_matches(version, requirement)1.16.0 - 检查语义化version是否满足requirement,例如,"0.1.0"是否匹配">=0.1.0";满足时返回"true",否则返回"false"。
样式
-
style(name)1.37.0 - 返回just使用的具名终端显示属性 转义序列。与包含标准颜色和样式的终端显示属性转义序列常量不同,style(name)返回的是just本身使用的转义序列,可用于让配方输出与just自身的输出样式保持一致。name可识别的值包括:用于回显配方行的'command'、error和warning。例如,要设置错误消息的样式:
scary: @echo '{{ style("error") }}OH NO{{ NORMAL }}'
用户目录1.23.0
这些函数会返回用户专属目录的路径,例如配置、数据、缓存、可执行文件目录 以及用户的主目录。
在 Unix 上,这些函数遵循 XDG 基本目录规范。
在 MacOS 和 Windows 上,这些函数会返回系统指定的用户专属
目录。例如,cache_directory() 在 MacOS 上返回 ~/Library/Caches,在
Windows 上返回 {FOLDERID_LocalAppData}。
有关更多详情,请参阅 dirs crate。
cache_directory()- 用户专属的缓存目录。config_directory()- 用户专属的配置目录。config_local_directory()- 用户专属的本地配置目录。data_directory()- 用户专属的数据目录。data_local_directory()- 用户专属的本地数据目录。executable_directory()- 用户专属的可执行文件目录。home_directory()- 用户的主目录。
如果你希望在所有平台上都使用 XDG 基本目录,可以使用带有相应环境变量和
回退值的 env(…) 函数。不过请注意,XDG 规范要求忽略非绝对路径;
因此,为了与符合规范的应用完全兼容,你需要这样做:
xdg_config_dir := if env('XDG_CONFIG_HOME', '') =~ '^/' {
env('XDG_CONFIG_HOME')
} else {
home_directory() / '.config'
}
常量
预定义了许多常量:
| 名称 | 值 | Windows 上的值 |
|---|---|---|
HEX1.27.0 | "0123456789abcdef" | |
HEXLOWER1.27.0 | "0123456789abcdef" | |
HEXUPPER1.27.0 | "0123456789ABCDEF" | |
PATH_SEP1.41.0 | "/" | "\" |
PATH_VAR_SEP1.41.0 | ":" | ";" |
CLEAR1.37.0 | "\ec" | |
NORMAL1.37.0 | "\e[0m" | |
BOLD1.37.0 | "\e[1m" | |
ITALIC1.37.0 | "\e[3m" | |
UNDERLINE1.37.0 | "\e[4m" | |
INVERT1.37.0 | "\e[7m" | |
HIDE1.37.0 | "\e[8m" | |
STRIKETHROUGH1.37.0 | "\e[9m" | |
BLACK1.37.0 | "\e[30m" | |
RED1.37.0 | "\e[31m" | |
GREEN1.37.0 | "\e[32m" | |
YELLOW1.37.0 | "\e[33m" | |
BLUE1.37.0 | "\e[34m" | |
MAGENTA1.37.0 | "\e[35m" | |
CYAN1.37.0 | "\e[36m" | |
WHITE1.37.0 | "\e[37m" | |
BG_BLACK1.37.0 | "\e[40m" | |
BG_RED1.37.0 | "\e[41m" | |
BG_GREEN1.37.0 | "\e[42m" | |
BG_YELLOW1.37.0 | "\e[43m" | |
BG_BLUE1.37.0 | "\e[44m" | |
BG_MAGENTA1.37.0 | "\e[45m" | |
BG_CYAN1.37.0 | "\e[46m" | |
BG_WHITE1.37.0 | "\e[47m" |
@foo:
echo {{HEX}}
$ just foo
0123456789abcdef
以 \e 开头的常量是
ANSI 转义序列。
CLEAR 会清除屏幕,效果类似于 clear 命令。其余常量的形式为
\e[Nm,其中 N 是整数,用于设置终端显示属性。
终端显示属性转义序列可以组合使用,例如文本粗细 BOLD、文本样式 STRIKETHROUGH、前景色 CYAN 和
背景色 BG_BLUE。这些序列之后应跟上 NORMAL,以将终端重置为正常状态。
转义序列应加引号,因为某些 shell 会将 [ 视为特殊字符。
@foo:
echo '{{BOLD + STRIKETHROUGH + CYAN + BG_BLUE}}Hi!{{NORMAL}}'
属性
Recipe、mod 语句和别名可以使用属性进行注释,以改变其行为。
| 名称 | 类型 | 描述 |
|---|---|---|
[arg(ARG, help="HELP")]1.46.0 | recipe | 在用法消息中为 ARG 打印帮助字符串 HELP。 |
[arg(ARG, long="LONG")]1.46.0 | recipe | 要求参数 ARG 的值通过 --LONG 选项传入。 |
[arg(ARG, short="S")]1.46.0 | recipe | 要求参数 ARG 的值通过短选项 -S 传入。 |
[arg(ARG, value="VALUE")]1.46.0 | recipe | 将选项 ARG 设为不接受值的标志。 |
[arg(ARG, pattern="PATTERN")]1.45.0 | recipe | 要求参数 ARG 的值匹配正则表达式 PATTERN。 |
[confirm]1.17.0 | recipe | 执行 recipe 前要求确认。 |
[confirm(PROMPT)]1.23.0 | recipe | 执行 recipe 前要求确认,并显示自定义提示。 |
[default]1.43.0 | recipe | 将此 recipe 设为模块的默认 recipe。 |
[doc(DOC)]1.27.0 | module, recipe | 将 recipe 或模块的文档注释设为 DOC。 |
[extension(EXT)]1.32.0 | recipe | 将 shebang recipe 脚本的文件扩展名设为 EXT。如需使用句点,EXT 应包含句点。 |
[group(NAME)]1.27.0 | module, recipe | 将 recipe 或模块放入组 NAME 中。 |
[linux]1.8.0 | recipe | 在 Linux 上启用 recipe。 |
[macos]1.8.0 | recipe | 在 MacOS 上启用 recipe。 |
[metadata(METADATA)]1.42.0 | recipe | 将 METADATA 附加到 recipe。 |
[no-cd]1.9.0 | recipe | 执行 recipe 前不切换目录。 |
[no-exit-message]1.7.0 | recipe | recipe 失败时不打印错误消息。 |
[no-quiet]1.23.0 | recipe | 覆盖全局静默 recipe 设置,始终回显 recipe。 |
[openbsd]1.38.0 | recipe | 在 OpenBSD 上启用 recipe。 |
[parallel]1.42.0 | recipe | 并行运行此 recipe 的依赖项。 |
[positional-arguments]1.29.0 | recipe | 为此 recipe 启用位置参数。 |
[private]1.10.0 | alias, recipe | 将 recipe、别名或变量设为私有。请参阅私有 Recipe。 |
[script]1.33.0 | recipe | 将 recipe 作为脚本执行。详情请参阅脚本 Recipe。 |
[script(COMMAND)]1.32.0 | recipe | 将 recipe 作为由 COMMAND 解释执行的脚本。详情请参阅脚本 Recipe。 |
[unix]1.8.0 | recipe | 在 Unix 系统上启用 recipe。(包括 MacOS。) |
[windows]1.8.0 | recipe | 在 Windows 上启用 recipe。 |
[working-directory(PATH)]1.38.0 | recipe | 设置 recipe 的工作目录。PATH 可以是相对路径或绝对路径。如果是相对路径,则相对于默认工作目录进行解析。 |
Recipe 可以拥有多个属性,这些属性可以分行书写:
[no-cd]
[private]
foo:
echo "foo"
也可以在同一行用逗号分隔1.14.0:
[no-cd, private]
foo:
echo "foo"
只有一个参数的属性可以使用冒号书写:
[group: 'bar']
foo:
启用和禁用 Recipe1.8.0
[linux]、[macos]、[unix] 和 [windows] 属性是配置属性。默认情况下,recipe 始终处于启用状态。带有一个或多个配置属性的 recipe 只有在一个或多个对应配置处于活动状态时才会启用。
这可用于编写会根据运行所在操作系统而表现不同的 justfile。此 justfile 中的 run recipe 会编译并运行 main.c,并根据操作系统使用不同的 C 编译器,以及该编译器对应的正确输出二进制文件名:
[unix]
run:
cc main.c
./a.out
[windows]
run:
cl main.c
main.exe
禁用更改目录1.9.0
just 通常会在当前目录设置为包含 justfile 的目录时执行配方。可以使用
[no-cd] 属性禁用此行为。这样可以创建使用相对于调用目录的路径,或对当前
目录进行操作的配方。
例如,以下 commit 配方:
[no-cd]
commit file:
git add {{file}}
git commit
可以使用相对于当前目录的路径,因为 [no-cd] 会阻止 just 在执行
commit 时更改当前目录。
要求配方运行前确认1.17.0
just 通常会执行所有配方,除非发生错误。[confirm]
属性允许配方在运行前要求在终端中确认。向 just 传递 --yes 可以覆盖此设置,
并自动确认所有标记了此属性的配方。
如果依赖的配方未获确认,则依赖于该配方的配方不会运行;任何要求确认的配方之后传入的配方也不会运行。
[confirm]
delete-all:
rm -rf *
自定义确认提示1.23.0
可以使用 [confirm(PROMPT)] 覆盖默认的确认提示:
[confirm("Are you sure you want to delete everything?")]
delete-everything:
rm -rf *
分组
配方和模块可以标注一个或多个组名:
[group('lint')]
js-lint:
echo 'Running JS linter…'
[group('rust recipes')]
[group('lint')]
rust-lint:
echo 'Running Rust linter…'
[group('lint')]
cpp-lint:
echo 'Running C++ linter…'
# not in any group
email-everyone:
echo 'Sending mass email…'
配方按组列出:
$ just --list
Available recipes:
email-everyone # not in any group
[lint]
cpp-lint
js-lint
rust-lint
[rust recipes]
rust-lint
just --list --unsorted 会按照各组在 justfile 中的顺序列出配方:
$ just --list --unsorted
Available recipes:
(no group)
email-everyone # not in any group
[lint]
js-lint
rust-lint
cpp-lint
[rust recipes]
rust-lint
可以使用 --groups 列出组:
$ just --groups
Recipe groups:
lint
rust recipes
使用 just --groups --unsorted 按照组在 justfile 中的顺序列出组。
使用反引号进行命令求值
可以使用反引号存储命令的结果:
localhost := `dumpinterfaces | cut -d: -f2 | sed 's/\/.*//' | sed 's/ //g'`
serve:
./serve {{localhost}} 8080
由三个反引号界定的缩进反引号,会以与缩进字符串相同的方式去除缩进:
# This backtick evaluates the command `echo foo\necho bar\n`, which produces the value `foo\nbar\n`.
stuff := ```
echo foo
echo bar
See the [Strings](#strings) section for details on unindenting.
Backticks may not start with `#!`. This syntax is reserved for a future
upgrade.
The [`shell(…)` function](#external-commands) provides a more general mechanism
to invoke external commands, including the ability to execute the contents of a
variable as a command, and to pass arguments to a command.
### Conditional Expressions
`if`/`else` expressions evaluate different branches depending on if two
expressions evaluate to the same value:
```just
foo := if "2" == "2" { "Good!" } else { "1984" }
bar:
@echo "{{foo}}"
$ just bar
Good!
也可以测试是否不相等:
foo := if "hello" != "goodbye" { "xyz" } else { "abc" }
bar:
@echo {{foo}}
$ just bar
xyz
也可以与正则表达式进行匹配:
foo := if "hello" =~ 'hel+o' { "match" } else { "mismatch" }
bar:
@echo {{foo}}
$ just bar
match
正则表达式由 regex crate 提供,其语法记录在 docs.rs 上。由于正则表达式 常用反斜杠转义序列,可以考虑使用单引号字符串字面量,这样斜杠就能原样传递给正则表达式解析器。
条件表达式会短路求值,这意味着只会对其中一个分支求值。这样可以确保反引号表达式在不应运行时不会运行。
foo := if env_var("RELEASE") == "true" { `get-something-from-release-database` } else { "dummy-value" }
可以在配方内部使用条件语句:
bar foo:
echo {{ if foo == "bar" { "hello" } else { "goodbye" } }}
可以串联多个条件语句:
foo := if "hello" == "goodbye" {
"xyz"
} else if "a" == "a" {
"abc"
} else {
"123"
}
bar:
@echo {{foo}}
$ just bar
abc
出错时停止执行
可以使用 error 函数中止执行。例如:
foo := if "hello" == "goodbye" {
"xyz"
} else if "a" == "b" {
"abc"
} else {
error("123")
}
运行后会产生以下错误:
error: Call to function `error` failed: 123
|
16 | error("123")
从命令行设置变量
可以从命令行覆盖变量。
os := "linux"
test: build
./test --test {{os}}
build:
./build {{os}}
$ just
./build linux
./test --test linux
可以在配方前传入任意数量的 NAME=VALUE 格式参数:
$ just os=plan9
./build plan9
./test --test plan9
也可以使用 --set 标志:
$ just --set os bsd
./build bsd
./test --test bsd
获取和设置环境变量
导出 just 变量
以 export 关键字为前缀的赋值会作为环境变量导出到配方:
export RUST_BACKTRACE := "1"
test:
# will print a stack trace if it crashes
cargo test
以 $ 为前缀的参数会作为环境变量导出:
test $RUST_BACKTRACE="1":
# will print a stack trace if it crashes
cargo test
导出的变量和参数不会导出到同一作用域中的反引号表达式。
export WORLD := "world"
# This backtick will fail with "WORLD: unbound variable"
BAR := `echo hello $WORLD`
# Running `just a foo` will fail with "A: unbound variable"
a $A $B=`echo $A`:
echo $A $B
设置 export 后,所有 just 变量都会作为环境变量导出。
取消导出环境变量1.29.0
可以使用 unexport keyword 取消导出环境变量:
unexport FOO
@foo:
echo $FOO
$ export FOO=bar
$ just foo
sh: FOO: unbound variable
从环境中获取环境变量
环境中的环境变量会自动传递给配方。
print_home_folder:
echo "HOME is: '${HOME}'"
$ just
HOME is '/home/myuser'
从环境变量设置 just 变量
可以使用 env() 函数将环境变量传递给 just 变量。
请参阅
environment-variables。
Recipe 参数
Recipe 可以有参数。这里的 build recipe 有一个名为
target 的参数:
build target:
@echo 'Building {{target}}…'
cd {{target}} && make
要在命令行传递参数,请将它们放在 recipe 名称之后:
$ just build my-awesome-project
Building my-awesome-project…
cd my-awesome-project && make
要向依赖项传递参数,请将依赖项和参数一起放在括号中:
default: (build "main")
build target:
@echo 'Building {{target}}…'
cd {{target}} && make
变量也可以作为参数传递给依赖项:
target := "main"
_build version:
@echo 'Building {{version}}…'
cd {{version}} && make
build: (_build target)
可以将命令的参数传递给依赖项,方法是将依赖项和参数一起放在 括号中:
build target:
@echo "Building {{target}}…"
push target: (build target)
@echo 'Pushing {{target}}…'
参数可以有默认值:
default := 'all'
test target tests=default:
@echo 'Testing {{target}}:{{tests}}…'
./test --tests {{tests}} {{target}}
带默认值的参数可以省略:
$ just test server
Testing server:all…
./test --tests all server
也可以提供这些参数:
$ just test server unit
Testing server:unit…
./test --tests unit server
默认值可以是任意表达式,但包含 +、&&、|| 或 / 运算符的表达式
必须加上括号:
arch := "wasm"
test triple=(arch + "-unknown-unknown") input=(arch / "input.dat"):
./test {{triple}}
Recipe 的最后一个参数可以是可变参数,在参数名称前加上 + 或
* 即可表示:
backup +FILES:
scp {{FILES}} [email protected]:
前缀为 + 的可变参数接受一个或多个参数,并展开为由空格分隔的
参数字符串:
$ just backup FAQ.md GRAMMAR.md
scp FAQ.md GRAMMAR.md [email protected]:
FAQ.md 100% 1831 1.8KB/s 00:00
GRAMMAR.md 100% 1666 1.6KB/s 00:00
前缀为 * 的可变参数接受零个或多个参数,并展开为由空格分隔的
参数字符串;如果没有参数,则展开为空字符串:
commit MESSAGE *FLAGS:
git commit {{FLAGS}} -m "{{MESSAGE}}"
可变参数可以设置默认值。这些默认值会被命令行传入的参数覆盖:
test +FLAGS='-q':
cargo test {{FLAGS}}
如果 {{…}} 替换内容中包含空格,可能需要加引号。例如,如果有以下 recipe:
search QUERY:
lynx https://www.google.com/?q={{QUERY}}
然后输入:
$ just search "cat toupee"
just 将运行命令 lynx https://www.google.com/?q=cat toupee,sh 会将其解析为
lynx、https://www.google.com/?q=cat 和 toupee,而不是预期的 lynx 和
https://www.google.com/?q=cat toupee。
添加引号即可解决此问题:
search QUERY:
lynx 'https://www.google.com/?q={{QUERY}}'
前缀为 $ 的参数会作为环境变量导出:
foo $bar:
echo $bar
可以使用 [arg("name", pattern="pattern")] 属性1.45.0,将参数限制为匹配正则表达式模式:
[arg('n', pattern='\d+')]
double n:
echo $(({{n}} * 2))
模式前会加上 ^,末尾会加上 $,因此它必须匹配整个参数值。
可以使用 | 运算符将模式限制为多个备选项:
[arg('flag', pattern='--help|--version')]
info flag:
just {{flag}}
正则表达式由 Rust regex crate 提供。
用法示例请参阅语法文档。
可以使用 --usage 子命令1.46.0打印 recipe 的用法信息:
$ just --usage foo
Usage: just foo [OPTIONS] bar
Arguments:
bar
可以使用 [arg(ARG, help=HELP)] 属性为参数添加帮助字符串:
[arg("bar", help="hello")]
foo bar:
$ just --usage foo
Usage: just foo bar
Arguments:
bar hello
配方标志和选项
配方参数默认采用位置参数形式。
在此 justfile 中:
@foo bar:
echo bar={{bar}}
参数 bar 是位置参数:
$ just foo hello
bar=hello
[arg(ARG, long=OPTION)]1.46.0 属性可用于将参数设为长选项。
在此 justfile 中:
[arg("bar", long="bar")]
foo bar:
参数 bar 使用 --bar 选项传入:
$ just foo --bar hello
bar=hello
也可以使用 --name=value 语法传递选项:
$ just foo --bar=hello
bar=hello
可以省略 long 的值,此时选项默认使用参数名称:
[arg("bar", long)]
foo bar:
[arg(ARG, short=OPTION)]1.46.0 属性可用于将参数设为短选项。
在此 justfile 中:
[arg("bar", short="b")]
foo bar:
参数 bar 使用 -b 选项传入:
$ just foo -b hello
bar=hello
如果参数同时具有长选项和短选项,可以使用其中任意一个传入。
可变参数 + 和 ? 不能设为选项。
[arg(ARG, value=VALUE, …)]1.46.0 属性可与 long 或 short 配合使用,将参数设为不接受值的标志。
在此 justfile 中:
[arg("bar", long="bar", value="hello")]
foo bar:
参数 bar 使用 --bar 选项传入,但该选项不接受值,而是使用 [arg] 属性中指定的值:
$ just foo --bar
bar=hello
这适用于强制要求在危险命令中使用 --force 之类的标志。
如果标志对应的参数有默认值,则该标志为可选:
[arg("bar", long="bar", value="hello")]
foo bar="goodbye":
未在调用中传入时,该参数会使用默认值:
$ just foo
bar=goodbye
依赖项
依赖项会在依赖它们的配方运行之前运行:
a: b
@echo A
b:
@echo B
$ just a
B
A
在一次 just 调用中,具有相同参数的配方只会运行一次,无论它在命令行调用中出现多少次,或作为依赖项出现多少次:
a:
@echo A
b: a
@echo B
c: a
@echo C
$ just a a a a a
A
$ just b c
A
B
C
多个配方可以依赖某个执行设置工作的配方;运行这些配方时,设置工作只会执行一次:
build:
cc main.c
test-foo: build
./a.out --test foo
test-bar: build
./a.out --test bar
$ just test-foo test-bar
cc main.c
./a.out --test foo
./a.out --test bar
只有在配方接收到相同参数时,它们才会在一次运行中被跳过:
build:
cc main.c
test TEST: build
./a.out --test {{TEST}}
$ just test foo test bar
cc main.c
./a.out --test foo
./a.out --test bar
在配方结束时运行配方
配方的普通依赖项总是在配方开始前运行。也就是说,被依赖的配方总是在依赖它的配方之前运行。这些依赖项称为“前置依赖项”。
配方也可以有后置依赖项,它们会在该配方运行后立即执行,并用 && 引入:
a:
echo 'A!'
b: a && c d
echo 'B!'
c:
echo 'C!'
d:
echo 'D!'
……运行 b 会输出:
$ just b
echo 'A!'
A!
echo 'B!'
B!
echo 'C!'
C!
echo 'D!'
D!
在配方中间运行配方
just 不支持在另一个配方运行过程中执行配方,但你可以在配方中间递归调用 just。给定以下 justfile:
a:
echo 'A!'
b: a
echo 'B start!'
just c
echo 'B end!'
c:
echo 'C!'
……运行 b 会输出:
$ just b
echo 'A!'
A!
echo 'B start!'
B start!
echo 'C!'
C!
echo 'B end!'
B end!
这种方式有一些限制,因为配方 c 是通过全新的一次 just 调用运行的:赋值会重新计算,依赖项可能会运行两次,而且命令行参数不会传递给子 just 进程。
Shebang 配方
以 #! 开头的配方称为 shebang 配方,执行时会先将配方正文保存到文件中,再运行该文件。这样你就可以用不同的语言编写配方:
polyglot: python js perl sh ruby nu
python:
#!/usr/bin/env python3
print('Hello from python!')
js:
#!/usr/bin/env node
console.log('Greetings from JavaScript!')
perl:
#!/usr/bin/env perl
print "Larry Wall says Hi!\n";
sh:
#!/usr/bin/env sh
hello='Yo'
echo "$hello from a shell script!"
nu:
#!/usr/bin/env nu
let hello = 'Hola'
echo $"($hello) from a nushell script!"
ruby:
#!/usr/bin/env ruby
puts "Hello from ruby!"
$ just polyglot
Hello from python!
Greetings from JavaScript!
Larry Wall says Hi!
Yo from a shell script!
Hola from a nushell script!
Hello from ruby!
在包括 Linux 和 MacOS 在内的类 Unix 操作系统上,执行 shebang 配方时,会先将配方正文保存到临时目录中的文件,将文件标记为可执行,然后执行该文件。随后,操作系统会将 shebang 行解析为命令行并调用该命令,同时传入文件路径。例如,如果配方以 #!/usr/bin/env bash 开头,操作系统最终运行的命令大致如下:/usr/bin/env bash /tmp/PATH_TO_SAVED_RECIPE_BODY。
shebang 行的拆分方式取决于操作系统。传入带参数的命令时,你可能需要使用 -S 标志,告知 env 显式拆分这些参数:
run:
#!/usr/bin/env -S bash -x
ls
Windows 不支持 shebang 行。在 Windows 上,just 会将 shebang 行拆分为命令和参数,将配方正文保存到文件中,然后调用拆分后的命令和参数,并将保存的配方正文路径作为最后一个参数。例如,在 Windows 上,如果配方以 #! py 开头,操作系统最终运行的命令大致如下:py C:\Temp\PATH_TO_SAVED_RECIPE_BODY。
脚本配方
带有 [script(COMMAND)]1.32.0 属性的配方会作为由 COMMAND 解释执行的脚本运行。这样可以避免 shebang 配方的一些问题,例如在 Windows 上使用 cygpath、需要使用 /usr/bin/env、不同 Unix 操作系统之间 shebang 行拆分方式不一致,以及需要使用可执行文件的临时目录。
带有空 [script] 属性的配方会使用 set script-interpreter := […]1.33.0 的值执行,默认为 sh -eu,而不是 set shell 的值。
配方正文会经过求值,写入临时目录中的磁盘文件,然后通过将其路径作为参数传给 COMMAND 来运行。
脚本和 Shebang 配方的临时文件
脚本和 shebang 配方都会将配方正文写入临时文件以供执行。脚本配方通过将该文件传给命令来执行,而 shebang 配方则直接执行该文件。如果临时文件所在的文件系统以 noexec 选项挂载,或因其他原因不可执行,shebang 配方将无法执行。
just 写入临时文件的目录可以通过多种方式配置,优先级从高到低如下:
- 通过
--tempdir命令行选项或JUST_TEMPDIR环境变量进行全局配置1.41.0。 - 通过
tempdir设置按模块配置。 - 在 Linux 上,通过
XDG_RUNTIME_DIR环境变量进行全局配置。 - 若以上均未配置,则使用 std::env::temp_dir 返回的目录。
使用 uv 编写 Python 配方
uv 是一款出色的跨平台 Python 项目管理器,以 Rust 编写。
使用 [script] 属性和 script-interpreter 设置,可以轻松配置 just,让它使用 uv 运行 Python 配方:
set unstable
set script-interpreter := ['uv', 'run', '--script']
[script]
hello:
print("Hello from Python!")
[script]
goodbye:
# /// script
# requires-python = ">=3.11"
# dependencies=["sh"]
# ///
import sh
print(sh.echo("Goodbye from Python!"), end='')
当然,也可以使用 shebang:
hello:
#!/usr/bin/env -S uv run --script
print("Hello from Python!")
更安全的 Bash Shebang 配方
如果你正在编写 bash shebang 配方,可以考虑添加 set -euxo pipefail:
foo:
#!/usr/bin/env bash
set -euxo pipefail
hello='Yo'
echo "$hello from Bash!"
虽然并非绝对必要,但 set -euxo pipefail 会启用几项实用功能,让 bash shebang 配方的行为更接近普通的逐行 just 配方:
set -e会使bash在命令失败时退出。set -u会使bash在变量未定义时退出。set -x会使bash在运行脚本的每一行之前将其打印出来。set -o pipefail会使bash在管道中的命令失败时退出。这是bash特有的功能,因此普通的逐行just配方不会启用它。
这些设置结合起来,可以避免许多 shell 脚本编写中的常见问题。
在 Windows 上执行 Shebang 配方
在 Windows 上,Shebang 解释器路径中包含 / 时,会使用随 Cygwin 提供的工具 cygpath,将 Unix 风格的路径转换为 Windows 风格的路径。
例如,要在 Windows 上执行此配方:
echo:
#!/bin/sh
echo "Hello!"
执行前,解释器路径 /bin/sh 会使用 cygpath 转换为 Windows 风格的路径。
如果解释器路径不包含 /,则不会进行转换,而是直接执行。如果 cygpath 不可用,或者你希望向解释器传递 Windows 风格的路径,这种方式会很有用。
在配方中设置变量
配方行由 shell 而非 just 解释,因此无法在配方中途设置 just 变量:
foo:
x := "hello" # This doesn't work!
echo {{x}}
虽然可以使用 shell 变量,但还有另一个问题:每个配方行都会由一个新的 shell 实例运行,因此在一行中设置的变量不会在下一行中生效:
foo:
x=hello && echo $x # This works!
y=bye
echo $y # This doesn't, `y` is undefined here!
解决此问题的最佳方法是使用 Shebang 配方。Shebang 配方的主体会被提取并作为脚本运行,因此整个配方会由同一个 shell 实例运行:
foo:
#!/usr/bin/env bash
set -euxo pipefail
x=hello
echo $x
在配方之间共享环境变量
每个配方的每一行都会由一个新的 shell 执行,因此无法在配方之间共享环境变量。
使用 Python 虚拟环境
某些工具(例如 Python 的 venv)需要加载环境变量才能运行,因此难以与 just 配合使用。作为替代方案,你可以直接执行虚拟环境中的二进制文件:
venv:
[ -d foo ] || python3 -m venv foo
run: venv
./foo/bin/python3 main.py
在配方中更改工作目录
每个配方行都会由一个新的 shell 执行,因此如果你在一行中更改工作目录,该更改不会影响后续行:
foo:
pwd # This `pwd` will print the same directory…
cd bar
pwd # …as this `pwd`!
有几种解决方法。一种是在要运行的命令所在的同一行中调用 cd:
foo:
cd bar && pwd
另一种方法是使用 Shebang 配方。Shebang 配方的主体会被提取并作为脚本运行,因此整个配方会由同一个 shell 实例运行;这样一来,一行中的 cd 就会影响后续行,与 shell 脚本中的行为相同:
foo:
#!/usr/bin/env bash
set -euxo pipefail
cd bar
pwd
缩进
配方行可以使用空格或制表符缩进,但不能混用。一个配方的所有行必须使用相同类型的缩进,但同一个 justfile 中的不同配方可以使用不同的缩进方式。
每个配方都必须比 recipe-name 至少多缩进一级,之后还可以进一步缩进。
以下是一个 justfile,其中一个配方使用空格缩进(以 · 表示),另一个使用制表符缩进(以 → 表示)。
set windows-shell := ["pwsh", "-NoLogo", "-NoProfileLoadTime", "-Command"]
set ignore-comments
list-space directory:
··#!pwsh
··foreach ($item in $(Get-ChildItem {{directory}} )) {
····echo $item.Name
··}
··echo ""
# indentation nesting works even when newlines are escaped
list-tab directory:
→ @foreach ($item in $(Get-ChildItem {{directory}} )) { \
→ → echo $item.Name \
→ }
→ @echo ""
PS > just list-space ~
Desktop
Documents
Downloads
PS > just list-tab ~
Desktop
Documents
Downloads
多行结构
没有初始 Shebang 的配方会逐行求值并运行,这意味着多行结构可能无法按预期工作。
例如,对于以下 justfile:
conditional:
if true; then
echo 'True!'
fi
conditional 配方第二行前面多出的缩进会导致解析错误:
$ just conditional
error: Recipe line has extra leading whitespace
|
3 | echo 'True!'
| ^^^^^^^^^^^^^^^^
要解决此问题,可以将条件语句写在一行中、用反斜杠转义换行符,或为配方添加 Shebang。以下列出了一些多行结构示例,供参考。
if 语句
conditional:
if true; then echo 'True!'; fi
conditional:
if true; then \
echo 'True!'; \
fi
conditional:
#!/usr/bin/env sh
if true; then
echo 'True!'
fi
for 循环
for:
for file in `ls .`; do echo $file; done
for:
for file in `ls .`; do \
echo $file; \
done
for:
#!/usr/bin/env sh
for file in `ls .`; do
echo $file
done
while 循环
while:
while `server-is-dead`; do ping -c 1 server; done
while:
while `server-is-dead`; do \
ping -c 1 server; \
done
while:
#!/usr/bin/env sh
while `server-is-dead`; do
ping -c 1 server
done
配方主体之外
带括号的表达式可以跨多行:
abc := ('a' +
'b'
+ 'c')
abc2 := (
'a' +
'b' +
'c'
)
foo param=('foo'
+ 'bar'
):
echo {{param}}
bar: (foo
'Foo'
)
echo 'Bar!'
以反斜杠结尾的行会延续到下一行,效果如同用空白字符将两行连接起来1.15.0:
a := 'foo' + \
'bar'
foo param1 \
param2='foo' \
*varparam='': dep1 \
(dep2 'foo')
echo {{param1}} {{param2}} {{varparam}}
dep1: \
# this comment is not part of the recipe body
echo 'dep1'
dep2 \
param:
echo 'Dependency with parameter {{param}}'
插值中也可以使用反斜杠续行。反斜杠后面的行必须缩进。
recipe:
echo '{{ \
"This interpolation " + \
"has a lot of text." \
}}'
echo 'back to recipe body'
命令行选项
just 提供了多个实用的命令行选项,可用于列出、转储和调试配方及变量:
$ just --list
Available recipes:
js
perl
polyglot
python
ruby
$ just --show perl
perl:
#!/usr/bin/env perl
print "Larry Wall says Hi!\n";
$ just --show polyglot
polyglot: python js perl sh ruby
使用环境变量设置命令行选项
某些命令行选项可以通过环境变量设置。
例如,可以通过 --unstable
标志启用不稳定功能:
$ just --unstable
也可以通过设置 JUST_UNSTABLE 环境变量启用:
$ export JUST_UNSTABLE=1
$ just
由于子进程会继承环境变量,通过环境变量设置的命令行选项会被 just 的递归调用继承,而通过参数设置的命令行选项则不会。
请参阅 just --help,了解哪些选项可以通过环境变量设置。
私有配方
名称以下划线 _ 开头的配方和别名不会显示在 just --list 中:
test: _test-helper
./bin/test
_test-helper:
./bin/super-secret-test-helper-stuff
$ just --list
Available recipes:
test
也不会显示在 just --summary 中:
$ just --summary
test
[private] 属性1.10.0也可用于隐藏配方或别名,无需更改其名称:
[private]
foo:
[private]
alias b := bar
bar:
$ just --list
Available recipes:
bar
这对于仅作为其他配方依赖项使用的辅助配方很有用。
静默配方
在配方名称前加上 @,即可反转每行前 @ 的含义:
@quiet:
echo hello
echo goodbye
@# all done!
现在,只有以 @ 开头的行会被回显:
$ just quiet
hello
goodbye
# all done!
可以使用 set quiet 让 Justfile 中的所有配方都静默执行:
set quiet
foo:
echo "This is quiet"
@foo2:
echo "This is also quiet"
[no-quiet] 属性会覆盖此设置:
set quiet
foo:
echo "This is quiet"
[no-quiet]
foo2:
echo "This is not quiet"
Shebang 配方默认静默执行:
foo:
#!/usr/bin/env bash
echo 'Foo!'
$ just foo
Foo!
在 Shebang 配方名称前加上 @,会让 just 在执行配方前打印该配方:
@bar:
#!/usr/bin/env bash
echo 'Bar!'
$ just bar
#!/usr/bin/env bash
echo 'Bar!'
Bar!
配方行执行失败时,just 通常会打印错误消息。可以使用 [no-exit-message]1.7.0 属性抑制这些错误消息。如果配方用于包装某个工具,这个属性尤其有用:
git *args:
@git {{args}}
$ just git status
fatal: not a git repository (or any of the parent directories): .git
error: Recipe `git` failed on line 2 with exit code 128
添加此属性后,工具以非零代码退出时,就不会显示退出错误消息:
[no-exit-message]
git *args:
@git {{args}}
$ just git status
fatal: not a git repository (or any of the parent directories): .git
使用交互式选择器选择要运行的配方
--choose 子命令会让 just 调用选择器,以选择要运行的配方。选择器应从标准输入读取包含配方名称的行,并将一个或多个名称以空格分隔后输出到标准输出。
由于目前无法通过 --choose 运行需要参数的配方,因此这类配方不会提供给选择器。私有配方和别名也会被跳过。
可以使用 --chooser 标志指定其他选择器。如果没有提供 --chooser,just 会先检查是否设置了 $JUST_CHOOSER。如果未设置,则默认使用热门的模糊查找工具 fzf。
可以在选择器中包含参数,例如 fzf --exact。
选择器的调用方式与配方行相同。例如,如果选择器是 fzf,它会通过 sh -cu 'fzf' 调用;如果指定了其他 shell 或 shell 参数,选择器调用也会遵循这些设置。
如果希望 just 默认使用选择器来选择配方,可以将以下内容用作默认配方:
default:
@just --choose
在其他目录中调用 justfile
如果传递给 just 的第一个参数包含 /,则会发生以下情况:
- 在最后一个
/处分割该参数。 - 将最后一个
/之前的部分视为目录。just会从该目录开始搜索justfile,而不是从当前目录开始。 - 将最后一个斜杠之后的部分视为普通参数;如果该部分为空,则会被忽略。
这看起来可能有些奇怪,但如果你想在子目录中的 justfile 里运行命令,这会很有用。
例如,如果你位于一个包含名为 foo 的子目录的目录中,foo 中有一个包含配方 build 的 justfile,且该配方也是默认配方,那么以下写法等效:
$ (cd foo && just build)
$ just foo/build
$ just foo/
第一个配方之后的其他配方也会在同一个 justfile 中查找。例如,以下两种写法等效:
$ just foo/a b
$ (cd foo && just a b)
二者都会调用 foo/justfile 中的配方 a 和 b。
导入
一个 justfile 可以通过 import 语句包含另一个文件的内容。
如果有以下 justfile:
import 'foo/bar.just'
a: b
@echo A
以及 foo/bar.just 中的以下文本:
b:
@echo B
foo/bar.just 将被包含在 justfile 中,并定义配方 b:
$ just b
B
$ just a
B
A
import 路径可以是绝对路径,也可以是相对于包含该路径的 justfile 所在位置的路径。导入路径开头的 ~/ 会被替换为当前用户的主目录。
justfile 不区分顺序,因此被包含的文件可以引用 import 语句之后定义的变量和配方。
导入的文件本身也可以包含 import,这些导入会被递归处理。
allow-duplicate-recipes 和 allow-duplicate-variables 分别允许重复的配方和变量相互覆盖,而不是报错。
在模块内,后面的定义会覆盖前面的定义:
set allow-duplicate-recipes
foo:
foo:
echo 'yes'
涉及 import 时,情况很遗憾会复杂得多,也很难解释。
较浅层的定义始终会覆盖较深层的定义,因此顶层的配方会覆盖导入文件中的配方,而导入文件中的配方会覆盖其导入的文件中的配方。
当导入的两个重复定义处于同一层级时,较早导入的定义会覆盖较晚导入的定义。
这是因为 just 在处理导入时会使用栈,按源代码中的顺序将导入推入栈中,并始终优先处理栈顶,因此较早的导入实际上会由编译器较晚处理。
这显然是个 bug,但由于 just 对向后兼容有非常严格的保证,而且我们竭尽全力避免破坏任何人的 justfile,因此我们创建了 issue #2540,以讨论是否真的可以修复这个问题。
在 import 关键字后加上 ?,可以使导入成为可选项:
import? 'foo/bar.just'
多次导入同一个源文件并不会报错1.37.0。这样就可以导入多个 justfile,例如 foo.just 和 bar.just;它们都导入了一个包含共享配方的第三个 justfile,例如 baz.just,而重复导入 baz.just 不会报错:
# justfile
import 'foo.just'
import 'bar.just'
# foo.just
import 'baz.just'
foo: baz
# bar.just
import 'baz.just'
bar: baz
# baz
baz:
模块1.19.0
justfile 可以通过 mod 语句声明模块。
mod 语句在 just1.31.0 中正式稳定。在更早的版本中,需要使用 --unstable 标志、set unstable,或设置 JUST_UNSTABLE 环境变量才能使用。
如果你的 justfile 如下:
mod bar
a:
@echo A
且 bar.just 中有以下内容:
b:
@echo B
bar.just 将作为子模块包含在 justfile 中。在一个子模块中定义的配方、别名和变量无法在另一个子模块中使用,并且每个模块都有自己的设置。
可以将子模块中的配方作为子命令调用:
$ just bar b
B
也可以使用路径语法:
$ just bar::b
B
如果模块名为 foo,just 会在 foo.just、foo/mod.just、foo/justfile 和 foo/.justfile 中查找模块文件。对于后两种情况,模块文件名可以使用任意大小写形式。
模块语句也可以采用以下形式:
mod foo 'PATH'
这会从 PATH 加载模块的源文件,而不是从通常的位置加载。如果 PATH 以 ~/ 开头,则会将其替换为当前用户的主目录。PATH 可以指向模块源文件本身,也可以指向包含名为 mod.just、justfile 或 .justfile 的模块源文件的目录。对于后两种情况,模块文件名可以使用任意大小写形式。
环境文件只会为根 justfile 加载,已加载的环境变量可在子模块中使用。子模块中影响环境文件加载的设置会被忽略。
不带 [no-cd] 属性的子模块配方运行时,会将工作目录设为包含该子模块源文件的目录。
即使从子模块配方中调用,justfile() 和 justfile_directory() 始终会返回根 justfile 的路径及其所在目录。
在 mod 关键字后添加 ?,即可将模块设为可选:
mod? foo
可选模块缺少源文件时不会报错。
没有源文件的可选模块之间不会冲突,因此可以有多个名称相同、但源文件路径不同的 mod 语句,只要最多只有一个源文件存在:
mod? foo 'bar.just'
mod? foo 'baz.just'
模块可以添加文档注释,这些注释会显示在 --list 输出中1.30.0:
# foo is a great module!
mod foo
$ just --list
Available recipes:
foo ... # foo is a great module!
模块仍然缺少许多功能,例如引用其他模块中的变量。更多信息请参阅模块改进跟踪问题。
隐藏 justfile
just 会查找名为 justfile 和 .justfile 的 justfile,因此可以使用 .justfile 来隐藏 justfile。
Just 脚本
在 justfile 顶部添加 shebang 行并使其可执行后,即可将 just 用作脚本解释器:
$ cat > script <<EOF
#!/usr/bin/env just --justfile
foo:
echo foo
EOF
$ chmod +x script
$ ./script foo
echo foo
foo
执行带 shebang 的脚本时,系统会将脚本路径作为参数传递给 shebang 中的命令。因此,如果 shebang 为 #!/usr/bin/env just --justfile,则命令将为 /usr/bin/env just --justfile PATH_TO_SCRIPT。
使用上述 shebang 时,just 会将工作目录切换到脚本所在位置。如果希望保持工作目录不变,请使用 #!/usr/bin/env just --working-directory . --justfile。
注意:不同操作系统对 shebang 行的拆分方式并不一致。以上示例仅在 macOS 上经过测试。在 Linux 上,可能需要向 env 传递 -S 标志:
#!/usr/bin/env -S just --justfile
default:
echo foo
格式化和转储 justfile
每个 justfile 都有规范的空白和换行格式。
你可以使用目前尚不稳定的 --fmt 标志,将当前 justfile 覆盖为规范格式:
$ cat justfile
# A lot of blank lines
some-recipe:
echo "foo"
$ just --fmt --unstable
$ cat justfile
# A lot of blank lines
some-recipe:
echo "foo"
调用 just --fmt --check --unstable 会以检查模式运行 --fmt。just 不会覆盖 justfile,而是在格式正确时以退出码 0 退出;如果格式不正确,则以退出码 1 退出并打印差异。
你可以使用 --dump 命令,将格式化后的 justfile 输出到 stdout:
$ just --dump > formatted-justfile
--dump 命令可以与 --dump-format json 配合使用,以打印 justfile 的 JSON 表示。
回退到父级 justfile
如果在某个 justfile 中找不到配方,且 fallback 设置已启用,just 就会在父目录及其上级目录中查找 justfile,直到到达根目录。遇到 fallback 设置为 false 或未设置的 justfile 后,just 就会停止查找。
例如,假设当前目录包含以下 justfile:
set fallback
foo:
echo foo
父目录包含以下 justfile:
bar:
echo bar
$ just bar
Trying ../justfile
echo bar
bar
避免参数拆分
给定以下 justfile:
foo argument:
touch {{argument}}
以下命令会创建两个文件:some 和 argument.txt:
$ just foo "some argument.txt"
用户的 shell 会将 "some argument.txt" 解析为单个参数,但当 just 将 touch {{argument}} 替换为 touch some argument.txt 时,引号不会保留,touch 会收到两个参数。
有几种避免此问题的方法:加引号、使用位置参数,以及导出参数。
加引号
可以在 {{argument}} 插值两侧添加引号:
foo argument:
touch '{{argument}}'
这样既保留了 just 在运行前捕获变量名拼写错误的能力,例如误写成 {{argument}},但如果 argument 的值中包含单引号,就无法按预期工作。
位置参数
positional-arguments 设置会将所有参数作为位置参数传递,以便通过 $1、$2、……和 $@ 访问;之后可以给它们加上双引号,避免 shell 进一步拆分:
set positional-arguments
foo argument:
touch "$1"
这样会失去 just 捕获拼写错误的能力,例如误将 $1 写成 $2;但适用于 argument 的所有可能值,包括包含双引号的值。
导出参数
设置 export 后,所有参数都会导出:
set export
foo argument:
touch "$argument"
也可以在单个参数前添加 $ 来导出:
foo $argument:
touch "$argument"
这样会失去 just 捕获拼写错误的能力,例如误写成 $argument;但适用于 argument 的所有可能值,包括包含双引号的值。
配置 Shell
有多种方式可以为逐行执行的配方配置 shell;当配方不以 #! shebang 开头时,默认使用这种执行方式。优先级从高到低如下:
--shell和--shell-arg命令行选项。传入其中任一选项后,just都会忽略当前 justfile 中的设置。set windows-shell := [...]set windows-powershell(已弃用)set shell := [...]
由于 set windows-shell 的优先级高于 set shell,你可以使用 set windows-shell 选择 Windows 上使用的 shell,并使用 set shell 选择其他所有平台上使用的 shell。
时间戳
just 可以在每条配方命令之前打印时间戳:
recipe:
echo one
sleep 2
echo two
$ just --timestamp recipe
[07:28:46] echo one
one
[07:28:46] sleep 2
[07:28:48] echo two
two
默认情况下,时间戳的格式为 HH:MM:SS。你可以通过 --timestamp-format 更改格式:
$ just --timestamp recipe --timestamp-format '%H:%M:%S%.3f %Z'
[07:32:11:.349 UTC] echo one
one
[07:32:11:.350 UTC] sleep 2
[07:32:13:.352 UTC] echo two
two
--timestamp-format 的参数是 strftime 风格的格式字符串。详情请参阅
chrono 库文档。
信号处理
信号 是发送给正在运行的程序、用于触发特定行为的消息。例如,按下 CTRL-C 时,会向终端前台进程组中的所有进程发送 SIGINT。
just 会尝试在收到信号时退出,但也会尽量避免留下仍在运行的子进程,这两个目标在一定程度上相互冲突。
如果 just 退出时留下了子进程,用户就只能通过 ps aux | grep 查找这些子进程,并手动 kill 它们,这是一件繁琐的事。
致命信号
用户关闭终端、按下 ctrl-c 或按下 ctrl-\ 时,分别会生成 SIGHUP、SIGINT 和 SIGQUIT,并将它们发送给前台进程组中的所有进程。
SIGTERM 是 kill 命令默认发送的信号,只会发送给指定的目标进程。
如果没有子进程在运行,just 收到上述任一信号后都会立即退出。
如果有子进程正在运行,just 会等待其终止,以免留下该进程。
此外,收到 SIGTERM 时,just 会将 SIGTERM 转发给所有正在运行的子进程1.41.0,因为与其他致命信号不同,SIGTERM 很可能只发送给了 just。
无论子进程在 just 收到致命信号后是否成功终止,just 都会停止执行。
SIGINFO
在包括 MacOS 在内的 BSD 衍生操作系统上,用户按下 ctrl-t 时,会向前台进程组中的所有进程发送 SIGINFO;Linux 不支持此信号。
just 会对此做出响应,打印所有子进程 ID 和命令的列表1.41.0。
Windows
在 Windows 上,用户按下 ctrl-c 时,just 的行为与收到 SIGINT 时相同。其他信号均不受支持。