プログラミング
エラー箇所を指し示す:uutils coreutils におけるコンパイラ風診断メッセージ
Pointing at the error: compiler-style diagnostics in uutils coreutils (uutils.org)
要約
uutils coreutils は、コマンドラインツールのエラー報告方法を刷新し、コンパイラのような詳細な診断メッセージを導入しました。これにより、エラーが発生した正確な引数や文字位置を、ケアット(^)記号とヘルプメッセージで分かりやすく示せるようになります。この機能は、tr、cut、chmod、sort、env、test、head、numfmt、csplit など、28のユーティリティで利用可能になり、ユーザーはエラーの原因を迅速に特定できるようになります。
全文翻訳
エラー箇所を指し示す:uutils coreutils におけるコンパイラ風診断メッセージ
Aug 31, 2026
Sylvestre Ledru
50年間、Coreutilsは進化を続けてきました。今、私たちはエラー報告の方法を再考することで、その革新をさらに推し進めています。
Unixツールは、エラーを標準エラー出力に1行で報告します。その行は、何が起こったかを伝えますが、どこで起こったかは示しません。ほとんどのコマンドでは他に指し示す場所はありませんが、テスト式、chmodモード、ソートキー、trセットのような小さな言語を取る引数がいくつかあります。
それらがパースに失敗した場合、実際に知りたいのは、パーサーがどの引数、あるいはその引数のどの文字でつまずいたかです。rustcは長年、ケアット(^)でその質問に答えており、ariadneは同じレンダリングを依存関係1つで提供しています。
コマンドラインツールにこのアイデアをもたらすきっかけとなったのは、uutils awkです。awkプログラムのエラーをそのように報告しています。coreutilsの引数はより小さな言語ですが、パースは同じように機能します。
0.11.0以降、coreutilsはこの機能を使用しています。標準エラー出力がターミナルである場合、パースエラーはレポートとして表示されます。引数がソース行としてエコーバックされ、ケアットが原因箇所をマークし、有用な情報があればヘルプ行が構文を説明します。
それがどのように見えるか
trから始めましょう。GNUのメッセージは、あなたがすでに照合順序(collating sequence)を知っていることを前提としています。
Before:
tr $ tr 'qw[y-b]' x
tr: range-endpoints of 'y-b' are in reverse collating sequence order
After:
tr $ tr 'qw[y-b]' x
tr: range-endpoints of 'y-b' are in reverse collating sequence order
╭─[ tr:1:7 ]
│ 1 │ tr qw[y-b] x
─┬─
╰─── did you mean 'b-y'?
│ │ Help: a range goes from the lower character to the higher one, as in a-z
───╯
プレイグラウンドで試してみてください。
カットリストは長くなることがあり、単一の不正な項目が含まれている場合があります。
Before:
cut $ cut -f 1,4-2,9-12 notes.txt
cut: invalid decreasing range
Try 'cut --help' for more information.
After:
cut $ cut -f 1,4-2,9-12 notes.txt
cut: invalid decreasing range
╭─[ cut:1:10 ]
│ 1 │ cut -f 1,4-2,9-12 notes.txt
─┬─
╰─── this range ends before it starts
│ │ Help: a list is N, N-M, N- or -M, separated by commas, as in -f1,4-6,9-
───╯
プレイグラウンドで試してみてください。
ケアットは引数全体をカバーする必要はありません。1文字を指すこともできます。
Before:
chmod $ chmod 'g+rw?x' notes.txt
chmod: invalid operator (expected +, -, or =, but found ?)
After:
chmod $ chmod 'g+rw?x' notes.txt
chmod: invalid operator (expected +, -, or =, but found ?)
╭─[ chmod:1:5 ]
│ 1 │ g+rw?x notes.txt
─
│ │ Help: a mode is either octal, as in 644, or clauses such as u+rwx,go-w
───╯
sortのキーは短いため、余分な文字を見落としやすいです。
Before:
sort $ sort -k2.3x notes.txt
sort: stray character in field spec: invalid field specification '2.3x'
After:
sort $ sort -k2.3x notes.txt
sort: stray character in field spec: invalid field specification '2.3x'
╭─[ sort:1:11 ]
│ 1 │ sort -k2.3x notes.txt
─
│ │ Help: a key is FIELD[.CHAR][OPTS][,FIELD[.CHAR][OPTS]], as in -k2.3,4nr
───╯
プレイグラウンドで試してみてください。
env -S は、シェルが分割するようなコマンドライン全体を受け取ります。古いメッセージは、問題のあるフラグメントを引用して返すことしかできませんでした。文字列にはスペースが含まれているため、引用符で囲んでエコーバックされ、ケアットは引用符の内側に配置されます。
Before:
env $ env -S 'echo ${1FOO}'
env: only ${VARNAME} expansion is supported, error at: ${1FOO}
After:
env $ env -S 'echo ${1FOO}'
env: only ${VARNAME} expansion is supported, error at: ${1FOO}
╭─[ env:1:14 ]
│ 1 │ env -S 'echo ${1FOO}'
─┬─
╰─── a variable name cannot start with a digit
│ │ Help: only $NAME and ${NAME} are expanded; the other shell forms are not
───╯
test は、式を個別の引数から構築します。レポートは、test を前に付けずに式自体をエコーバックし、それを壊した引数をマークします。
Before:
test $ test 7 -eq zap
test: invalid integer 'zap'
After:
test $ test 7 -eq zap
test: invalid integer 'zap'
╭─[ test:1:7 ]
│ 1 │ 7 -eq zap
───
│ │ Help: -eq, -ne, -lt, -le, -gt and -ge compare integers; use =, !=, < or > to compare strings
│ -eq equal, -ne not equal, -lt less than, -le less than or equal, -gt greater than, -ge greater than or equal
───╯
プレイグラウンドで試してみてください。
SIZE は、数値と単位で構成されます。レポートは、拒否された半分を示します。
Before:
head $ head -c 1fb notes.txt
head: invalid number of bytes: '1fb'
After:
head $ head -c 1fb notes.txt
head: invalid number of bytes: '1fb'
╭─[ head:1:10 ]
│ 1 │ head -c 1fb notes.txt
─┬
╰── not a known unit
│ │ Help: a size is a number and an optional unit: K, M, G and so on for 1024, KB, MB, GB for 1000
───╯
プレイグラウンドで試してみてください。
1つのパーサーがスイート内のすべてのSIZEを処理するため、tail -c、truncate -s、split -b、shred -s、od -N、sort -S、du -B、df -B、ls --block-sizeのブロックサイズ、およびdu -tの閾値で同じレポートが表示されます。
numfmt --format は printf スタイルのフォーマットで、変換は1つだけ許可されます。古いメッセージはルールを繰り返すだけでした。アノテーションは、実際に記述した変換を指定します。
Before:
numfmt $ numfmt --format=%q 1000
numfmt: invalid format '%q', directive must be %[0]['][-][N][.][N]f
After:
numfmt $ numfmt --format=%q 1000
numfmt: invalid format '%q', directive must be %[0]['][-][N][.][N]f
╭─[ numfmt:1:18 ]
│ 1 │ numfmt --format=%q 1000
┬
╰── f is the only conversion numfmt has; %d, %e, %g and the other C conversions are not accepted
│ │ Help: a format is [PREFIX]%[0]['][-][WIDTH][.PRECISION]f[SUFFIX], as in "%' -10.2f"
───╯
プレイグラウンドで試してみてください。
csplit のパターンには正規表現が含まれており、正規表現エンジンはどの文字で失敗したかを知っています。私たちはその位置情報を単に捨てていました。
Before:
csplit $ csplit notes.txt '/a{2,1}/'
csplit: '/a{2,1}/': invalid pattern
After:
csplit $ csplit notes.txt '/a{2,1}/'
csplit: '/a{2,1}/': invalid pattern
╭─[ csplit:1:20 ]
│ 1 │ csplit notes.txt /a{2,1}/
──┬──
╰──── invalid repetition count range, the start must be <= the end
│ │ Help: a pattern is a line number N, /REGEXP/[OFFSET] or %REGEXP%[OFFSET], each optionally followed by {N} or {*}
───╯
プレイグラウンドで試してみてください。
そのラベルは正規表現エンジンから直接取得され、その単語が存在する唯一の場所であるため、翻訳されません。
適用箇所
0.11.0では28のユーティリティがこれを使用しています。リンクされた例はプレイグラウンドで実行できます。それ以外のものは、WebAssemblyビルドでは出荷されないユーティリティ用なので、ローカルで試してください。
Utility | What the caret points at | Try it
------- | ------------------------ | ------
test | the argument that made the expression fail | test 7 -eq zap
expr | the argument that made the expression fail | expr 9 + foo
chmod | the failing clause (or character) of an invalid symbolic or octal mode | chmod 'g+rw?x' fruits.txt
mkdir | the failing part of the mode given to -m/--mode | mkdir -m u+q mydir
mkfifo | the failing part of the mode given to -m/--mode | mkfifo -m u+q mypipe
mknod | the failing part of the mode given to -m/--mode | mknod -m u+q mydev c 1 3
install | the failing part of the mode given to -m/--mode | install -m u+q fruits.txt
dest | tr | the part of a set that is at fault (bad class, backwards range, bad repeat count, …) | tr 'qw[y-b]' x
sort | the failing part of a -k/--key or field specification, or of the SIZE given to -S | sort -k2.3x fruits.txt
numfmt | the failing part of a --format or --field specification, the value given to --from, --to, --from-unit, --to-unit, --padding or --header, or the input number itself | numfmt --format=%q 1000
printf | the failing conversion or escape in the format string | printf %5.2c q
seq | the failing conversion in the format given to -f/--format | seq -f %5.2c 1 3
stat | the failing directive of a -c/--format or --printf format | stat -c %d%.3 fruits.txt
env | the failing part of a -S/--split-string string | env -S 'echo ${1FOO}'
dd | the failing key, value or flag of a KEY=VALUE operand | dd conv=ucase,zap
join | the failing field of the output format given to -o | join -o 1.2,2.x fruits.txt fruits.txt
cut | the failing range in the list given to -b, -c, -f or -F | cut -f 1,4-2 fruits.txt
csplit | the failing pattern operand, the character of its regex that broke, or the format given to -b/-n | csplit fru