Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions crates/bashkit/src/builtins/vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ impl Builtin for Shopt {
for opt in SHOPT_OPTIONS {
let key = format!("SHOPT_{}", opt);
let on = ctx.variables.get(&key).map(|v| v == "1").unwrap_or(false);
output.push_str(&format!("{:<32}{}\n", opt, if on { "on" } else { "off" }));
output.push_str(&format!("{:<15}\t{}\n", opt, if on { "on" } else { "off" }));
}
return Ok(ExecResult::ok(output));
}
Expand Down Expand Up @@ -655,7 +655,11 @@ impl Builtin for Shopt {
for opt in SHOPT_OPTIONS {
let key = format!("SHOPT_{}", opt);
let on = ctx.variables.get(&key).map(|v| v == "1").unwrap_or(false);
output.push_str(&format!("{:<32}{}\n", opt, if on { "on" } else { "off" }));
output.push_str(&format!(
"{:<15}\t{}\n",
opt,
if on { "on" } else { "off" }
));
}
return Ok(ExecResult::ok(output));
}
Expand All @@ -672,7 +676,7 @@ impl Builtin for Shopt {
}
let key = format!("SHOPT_{}", opt);
let on = ctx.variables.get(&key).map(|v| v == "1").unwrap_or(false);
output.push_str(&format!("{:<32}{}\n", opt, if on { "on" } else { "off" }));
output.push_str(&format!("{:<15}\t{}\n", opt, if on { "on" } else { "off" }));
}
if any_invalid {
Ok(ExecResult {
Expand Down
10 changes: 10 additions & 0 deletions crates/bashkit/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1720,6 +1720,16 @@ impl<'a> Parser<'a> {
words.push(Word::literal(")"));
self.advance();
}
// Inside `[[ ]]` these are string comparison operators, not
// redirections (bash's conditional grammar has no redirects).
Some(tokens::Token::RedirectIn) => {
words.push(Word::literal("<"));
self.advance();
}
Some(tokens::Token::RedirectOut) => {
words.push(Word::literal(">"));
self.advance();
}
None => {
return Err(crate::error::Error::parse(
"unexpected end of input in [[ ]]".to_string(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
### dbracket_string_order
[[ a < B ]]; echo "a<B:$?"
[[ B < a ]]; echo "B<a:$?"
[[ a > B ]]; echo "a>B:$?"
[[ 9 < 10 ]]; echo "9<10:$?"
[[ "" < B ]]; echo "empty:$?"
### expect
a<B:1
B<a:0
a>B:0
9<10:1
empty:0
### end

### dbracket_string_order_with_expansions
x=apple; y=Banana
[[ $x < $y ]]; echo "$?"
[[ "$x" > "$y" ]] && echo gt
[[ $x < $y || $x > $y ]] && echo ordered
### expect
1
gt
ordered
### end

### shopt_listing_width
shopt extglob nullglob | cat -A
shopt -s extglob
shopt extglob
shopt -u extglob
### expect
extglob ^Ioff$
nullglob ^Ioff$
extglob on
### end
5 changes: 2 additions & 3 deletions crates/bashkit/tests/spec_cases/bash/variables.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -629,13 +629,12 @@ exit:1

### shopt_show_specific
# shopt shows status of named option
### bash_diff
shopt nullglob
shopt -s nullglob
shopt nullglob
### expect
nullglob off
nullglob on
nullglob off
nullglob on
### end

### shopt_nullglob_no_matches
Expand Down
1 change: 1 addition & 0 deletions knowledge/log.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* **Fix**: the `bashkit` CLI exits 2 (was 1) when a script or `-c` string has a syntax error before any runnable command, printing `bash: syntax error: ...` instead of the anyhow `Error: Failed to execute script` chain. Matches the partial-run path, which already exited 2. See foundations/parser "Partial execution before a syntax error".
* **Fix**: `eval` with a syntax error exits 2 like bash and reports `bash: eval: line N: syntax error: ...` plus the offending line, each newline-terminated, with the eval's own redirects applied (`$(eval bad 2>&1)` captures it). `$LINENO` inside `$( )` now counts from the outer line instead of restarting at 1. The parser's message text still differs from bash's `near unexpected token` wording.
* **Fix**: parameter expansion and `mapfile` gaps from the Debian oracle. `${v~}`/`${v~~}` toggle case (also per array element). `\/` in a `${v/pat/rep}` replacement is a plain `/`. Unquoted `${!prefix@}`, `${!ref}`, `${v:off:len}`, `${a[@]:o:n}` and `${v@op}` now undergo field splitting like other expansions, so `for v in ${!cfg_@}` loops per name. `mapfile` gained `-d DELIM` (`''` is NUL), `-n`, `-s`, `-O` (keeps other elements) and grouped options; `-u` other than 0 and `-C`/`-c` exit 2 (L-MAPFILE-001).
* **Fix**: `[[ a < b ]]` and `[[ a > b ]]` compare strings instead of being swallowed as redirections (the conditional grammar has no redirects), so `[[ a < B ]]` is false in byte order like bash. `shopt` listings pad the name to 15 columns and separate the state with a tab, matching bash.
* **Decision**: CPython's workload priority is agents running `python3` for scripted logic (processing a file, transforming data). Low-use stdlib modules are no longer shipped: `unittest`, `doctest`, profilers, `compileall`/`zipapp`, `dbm`/`shelve`, `plistlib`, `wave`, `netrc`, `cmd`, `tty`/`pty`, and `bz2`/`lzma`/`compression.zstd` (no C codec in the guest anyway). Stdlib zip 3.58 to 3.16 MB. L-CPY-009 updated. See [CPython WebAssembly Runtime](runtimes/cpython-wasm.md#workload-priority).
* **Feature**: `yes` and `seq` stream into pipelines. As a producer stage they write chunks into the pipe as they go (`Context::stdout_stream`), so `yes | head -1` and `seq 1000000 | head -1` stop after the first chunk with `PIPESTATUS` `141 0` instead of running to the output cap and printing a truncation notice. Step 2 of the roadmap (streaming builtin I/O), starting with the generators.
* **Feature**: `sudo` and `busybox`, the top missing commands in the gap replay (`sudo tee /etc/x <<EOF`, `busybox tar --help`). `sudo` skips its options and runs the command (leading `VAR=VALUE` through `env`, `-s`/`-i` through `bash -c`; `-v`/`-k` succeed, `-l` lists ALL); `busybox APPLET` runs the builtin of that name, `--list` lists them (L-SUDO-001). `nohup`/`nice`/`flock` no longer steal the inner command's `--help`.
Expand Down
Loading