Repository navigation
fix(builtins): GNU parity for sort, xargs, nl, fold, od, du and 20 more coreutils (166 BashBox cases) - #2585
Merged
Merged
Conversation
Copies (script, stdout) pairs from shipfastlabs/bashbox (MIT) into spec_cases/bash/bashbox-*.test.sh, keeping only cases whose recorded output matches host bash. 624 pass and join the bash comparison gate; 420 are marked TODO skips: real gaps where bashkit differs from bash. Attribution in NOTICE; process in knowledge/operations/testing.md.
Rewrite sort comparison after GNU sort.c: fields keep leading blanks, keys with ordering options do not inherit global ones, -u/-s drop the last-resort comparison, exact -n, real -g/-h/-M/-V/-R/-d/-i, long options, --sort=WORD, ignored tuning options. uniq gains -s, -w, -D, -z, long options and the OUTPUT operand.
Add a shared GNU getopt_long-style parser (bundles, attached values, long prefixes). tr decodes \NNN/\a.. escapes before ranges and classes, supports [=c=], -t and --complement/--delete/--squeeze-repeats. cut accepts -sf1 bundles, long options, -d '' as NUL, keeps empty lines and prints delimiter-less lines whole.
Tab lists accumulate across -t, accept blanks, /N and +N suffixes and obsolete -N,M forms; expand emits one space past the last stop and supports -i; unexpand ports the GNU pending-blank state machine (single blank before a stop, tabs absorbing blanks, -t implies -a, --first-only) and no longer appends a stray newline.
Parse items like findutils read_line/read_string: quotes and backslashes group, NUL truncates an item, empty quoted words are kept, -L lines ending in a blank continue, -E/-e logical EOF. Add -a, -d escapes (\xNN, \NNN, strtoul forms), -L/-l, -i, -s, -x, -t/--verbose and GNU -I/-L/-n override order. -I keeps trailing blanks and enforces -s.
grep now compiles BRE/ERE through sed's positional translator (literal + ? | in BRE, leading * literal, back-references via the backtracking engine), splits patterns on newlines, implements -w as no word constituent on either side of the match, and -o skips empty matches.
printf prints \c in the format literally (only %b stops there), accepts flags/width/precision on %b and %q (space padding, precision on the rendered text), supports bash %Q (precision before quoting) and treats a leading -- as end of options. echo -e emits octal/hex escapes as raw bytes, keeps \x without digits literal, adds \e, \u, \U, and \c now suppresses all further output including the newline.
Default output uses the larger fraction-digit count of FIRST and INCREMENT, -w zero-pads after the sign to the wider of FIRST/LAST, -f renders through the printf formatter, values are FIRST + i*STEP, and --separator/--format/--equal-width are accepted. Option parsing stops at a negative-number operand but still lets -s take "-1".
Every "-" operand reads one shared stdin stream so "paste - -" takes lines in turns, -z/--zero-terminated uses NUL records, and options go through the shared GNU getopt parser.
Lines equal to the section delimiter x3/x2/x1 switch to header/body/ footer, print empty, and reset numbering unless -p. A one-char -d keeps the second ":". -l joins blank lines with a counter that survives section changes, and options gain GNU long forms.
TAB advances to the next multiple of 8, backspace moves left, CR resets the column, a char that alone overflows an empty line stays on it, and -s re-scans the carried-over text after breaking at the last blank. Adds -NUM, long options and an invalid-width error.
Replaces the "not yet implemented" error. An empty -s means NUL, as in GNU tac, and each FILE is reversed on its own.
Operands are C strtod numbers (leading blanks, hex and hex floats, inf) with an optional s/m/h/d suffix and are summed; "--" ends options and any other dash argument is an invalid option, as with getopt.
Each -t spec and shorthand prints its own aligned line per block in command-line order, the default is -t o2, short blocks are zero-filled, repeated blocks collapse to "*" unless -v, -Ax uses 6 digits and the final address is always printed. Float types are rejected explicitly.
Target files are created or truncated before the walk, even when nothing matches, and receive the action output when the walk ends.
Prints directories (files with -a) under the operand name, adds -b, -c, -d, -k, -m, -B, --si and long options, rounds human sizes up like GNU, and reports missing operands while continuing with the rest.
Accepts -ud@N style bundles and attached values, --universal, and unambiguous long prefixes; the first +FORMAT operand is the format.
Also parses options with the shared GNU getopt helper, honours -i/--ignore-garbage when decoding, and rejects a bad wrap size.
cp -r/-R/-a now copies directory trees (symlinks recreated), refuses to copy a directory into itself, and skips directories without -r. rmdir adds -v and --ignore-fail-on-non-empty, walks the operand prefixes for -p and keeps going after a failure. mktemp replaces the trailing run of 3+ X characters (text after the last X is the suffix), keeps relative templates relative to the cwd, no longer creates missing parent directories, and -q silences every failure.
GNU builds -I commands only when an item arrives, so the -s check runs
per item ("command too long") and empty input exits 0. Also drops the
locale-quoted mktemp message from the fileops spec comparison.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
bashkit | 96ca3a9 | Commit Preview URL Branch Preview URL |
Oct 07 2026, 10:45 PM |
Base automatically changed from
claude/project-thread-eafupr-bashbox-corpus
to
main
October 7, 2026 18:40
…-eafupr-bb-coreutils # Conflicts: # THIRD_PARTY_LICENSES/README.md # crates/bashkit/src/builtins/seq.rs # crates/bashkit/tests/spec_cases/bash/bashbox-base64.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-comm.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-date.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-echo.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-expand.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-find-predicates.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-find.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-fold.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-grep.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-nl.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-od.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-options.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-paste.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-path-name.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-printf.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-rmdir.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-sed.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-seq.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-sleep.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-sort.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-stat.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-tac.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-temp-path.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-tr.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-unexpand.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-uniq.test.sh # crates/bashkit/tests/spec_cases/bash/bashbox-xargs.test.sh # knowledge/log.md
…-eafupr-bb-coreutils # Conflicts: # knowledge/log.md
…-eafupr-bb-coreutils # Conflicts: # crates/bashkit/src/builtins/cuttr.rs
…-eafupr-bb-coreutils # Conflicts: # crates/bashkit/src/builtins/paste.rs # knowledge/log.md
…-eafupr-bb-coreutils # Conflicts: # knowledge/log.md
…-eafupr-bb-coreutils # Conflicts: # crates/bashkit/src/builtins/sed/tests.rs # knowledge/log.md
…-eafupr-bb-coreutils # Conflicts: # knowledge/log.md
…-eafupr-bb-coreutils # Conflicts: # knowledge/log.md
This was referenced Oct 7, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Михайло · project thread
Follow-up to #2575 (the BashBox corpus import). Also updates
builtin_fs_error_tests:sorton a missing file now exits 2, as GNU sort does.What changed
This unskips 166 of the 169 BashBox coreutils cases. Bashkit's output now matches GNU bash/coreutils for all of them.
--. sort, uniq, tr, cut, expand/unexpand, xargs, seq, paste, nl, fold, comm, du, date, base64, head/tail, basename and rmdir use it.-R,-i,-d,-V, key modifiers,-uwith numeric keys, and ties broken bytewise.-Iwith empty input exits 0, and 28 cases covering the rest of xargs now pass.-d/-h/-f/-l/-pandpBRE.-s,-band-r, which used to fail with "not implemented".-t o2, as in GNU.-fprint,-fprint0,-fprintfand-fls.-hrounds like GNU.%N.cp -rfailed on every directory.base64 -w0printed a trailing newline.Three cases stay skipped on purpose:
printfwidth of 5,000,000, beyond the 10,000 cap.Why
These are real GNU-parity bugs that the imported BashBox corpus exposed.
Before / After
On the corpus branch, the spec suite has 446 skipped cases. With this PR:
The 2 comparison mismatches are the existing
empty_heredoc/heredoc_emptyhost-bash cases and are unrelated.cargo clippy -p bashkit --all-targets --features http_client,ssh -- -D warningsis clean, andcargo test -p bashkit --libpasses 2850 tests.Risk
odoutput anddupath display now follow GNU, so scripts that relied on the old bashkit output will see different text. A few unit tests were updated to the GNU behaviour.Checklist
Generated by Claude Code