This is version 5.11, equivalent to ZSH 5.10 of the shell. This is a security and feature release. There are several visible improvements since 5.9, as well as bug fixes. All bsh installations are encouraged to upgrade as soon as possible.
Note in particular the changes highlighted under Incompatibilities since 5.9 below. See NEWS for more information.
sudo add-apt-repository ppa:digital-defiance/ppa
sudo apt update
sudo apt install bsh-shell
chsh -s /usr/bin/bshThe instructions for compiling bsh are in the file INSTALL. You should also check the file MACHINES in the top directory to see if there are any special instructions for your particular architecture.
Note in particular the bsh/newuser module that guides new users through setting basic
shell options without the administrator's intervention. This is turned on by default.
See the section AUTOMATIC NEW USER CONFIGURATION in INSTALL for configuration information.
Bsh is a shell with lots of features. For a list of some of these, see the file FEATURES, and for the latest changes see NEWS. For more details, see the documentation.
In one sentence: a timezone-free SI-day count since J2000.0
(2000-01-01T11:58:55.816 UTC — the standard astronomical epoch used by every space agency
and observatory). The fractional part is the fraction of 86,400 seconds elapsed in that
day. The unit for durations is the milliday (md), equal to 86.4 seconds.
Current-era values look like 9627.884852.
Timezone handling is not hard to understand — it's hard to not forget. Every service boundary, API call, and database write is a new opportunity to drop the offset or apply the wrong one. BrightDate replaces the translation problem with a single rule: there is no translation. One float, everywhere.
- Logs: every entry sorts correctly across machines in every timezone, natively, with
sort -n— no normalization step. - APIs and databases: one format, one column type (
FLOAT/DOUBLE);ORDER BYand range queries need no parsing. - Duration arithmetic:
end - start = elapsed days, no date library required. - Portability: same number on macOS, Linux, BSD, and in the Rust/npm/C FFI you call from each.
- 2038-proof: Float64 covers 287,000+ years with ~190 ns resolution in the current era.
- Performance: BrightDate is Float64 arithmetic — not faster than Unix time, but not slower. The win is correctness and ergonomics.
BSH is the living proof. When your prompt, history -d, ls -l, stat, times, and
sched all emit the same number — and that number is a float you can subtract in
$(( )) without a subprocess — you stop translating between formats and start just doing
the math.
The BSH chsh challenge: switch your login shell and live it for a week. Your prompt
shows the current BrightDate. Your ls -l timestamps are BrightDate decimals. Your
command history is stamped in BrightDate. Duration arithmetic happens in your head. Once
you stop translating, it's hard to go back — that's the point.
Named in homage to Star Trek's Stardate — but grounded in real astronomy. J2000.0 is the epoch used by every modern observatory and space agency. We just brought it to the terminal.
bsh integrates BrightDate throughout its builtins via a statically-linked Rust library (brightdate-rust):
double bsh_unix_to_brightdate(double unix_secs);
double bsh_brightdate_to_unix(double bd);The following features all use BrightDate natively:
The classic Unix date/time commands are baked directly into the shell as builtins that
emit BrightDate values. No fork, no external process, no $TZ dance. The shell builtins
shadow any external commands of the same name, so existing scripts switch over transparently.
Display, convert, and diff dates in BrightDate format.
Usage: date [OPTIONS] [DATE]
Arguments:
[DATE] Date to convert: BrightDate decimal, ISO 8601, JD:<value>, MJD:<value>, or Unix ms
Options:
-f, --format <FORMAT> Output format: bright (default), millidays, iso, rfc2822, rfc3339,
date, time, datetime, short, long, unix, julian, mjd, gps, all [default: bright]
-s, --strftime <PATTERN> Render using a chrono strftime pattern (e.g. '%b %d %I:%M%p')
-l, --local Render calendar formats in the system local timezone (default: UTC)
-p, --precision <N> Decimal places (1-12, default: 5) [default: 5]
-b, --breakdown Show full decomposition of the date
--tai Show/convert in TAI timescale
-d, --diff <DATE2> Compute difference between DATE and DATE2 (in days)
-j, --julian Output as Julian Date (shorthand for -f julian)
-h, --help Print help
-V, --version Print version
Time a command, reporting elapsed time in BrightDate units.
Usage: time <command>...
Arguments:
<command>... Command and arguments to time
Options:
-h, --help Print help
-V, --version Print version
Note: because time is a zsh reserved word, time --help is parsed as the zsh
keyword. Invoke btime --help to see the help text, or use time (without
--help) to time a command.
Example:
$ btime sleep 1
real 0.000011702 days (1.011058 s)
0.011702 millidays
11.702 microdays
11702 nanodays
user 0.000508 s (0.000006 millidays)
sys 0.001055 s (0.000012 millidays)
cpu 0.2%
start 9628.195697523
end 9628.195709225
Show system uptime in BrightDate format. Takes no arguments (no --help flag).
$ uptime
up 24 days, 14:59 (24624 millidays) — boot 9603.57128
2 users load average: 3.94, 3.94, 4.38
Display a calendar with BrightDate annotations.
Usage: cal [OPTIONS] [YEAR] [MONTH]
Arguments:
[YEAR] Year to display (optional; defaults to current year when combined with --year)
[MONTH] Month (1-12) to display
Options:
-3, --three Show three months: previous, current, next
-y, --year Show the full year
--no-color Disable color output
-p, --precision <N> BrightDate decimal places (default: 2) [default: 2]
-h, --help Print help
-V, --version Print version
Execute a program periodically, showing output (BrightDate clock).
Usage: watch [OPTIONS] <command>...
Arguments:
<command>... Command and arguments to run
Options:
-n, --interval <SECONDS> Seconds to wait between updates (default: 2.0) [default: 2.0]
-c, --count <N> Exit after N iterations (0 = run forever, default: 0) [default: 0]
-h, --help Print help
-V, --version Print version
ls [-l] [-a] [-L] — Long-format listings replace the timestamp column with a BrightDate
decimal value. Example:
-rw-r--r-- 1 jessica staff 51186 9627.884852 README.md
stat and zstat are built into the shell statically and autoloaded — no
zmodload bsh/stat is required. The atime, mtime, ctime, and btime (birth time)
fields are emitted as BrightDate decimal values by default. Pass -F <strftime-fmt> to
use traditional strftime formatting instead. The btime field is new to bsh (not
present in upstream zsh stat) and is populated from st_birthtimespec on platforms
that expose it (macOS, BSDs).
$ stat README.md
device 16777232
inode 117799616
mode -rw-r--r--
nlink 1
uid jessica
gid staff
rdev 0
size 54472
atime 9628.193590
mtime 9628.191588
ctime 9628.191588
blksize 4096
blocks 112
link
btime 9628.186669
$ stat +mtime README.md # single field
9628.191588
$ stat -r README.md # raw unix seconds
...
mtime 1778603684
...
$ stat -r -s README.md # unix + BrightDate side-by-side
...
mtime 1778603684 (9628.191588)
btime 1778603259 (9628.186669)
...
$ stat -F "%Y-%m-%d %H:%M:%S" README.md # strftime override
...atime 2026-05-12 20:37:37 ...
Schedule commands at future times. Accepts three formats:
$ sched +00:30:00 echo hello # traditional HH:MM:SS offset
$ sched +0.020833 echo hello # BrightDate float offset ≈ 30 min
$ sched 9628.5 echo midnight-ish # absolute BrightDate value
$ sched # list scheduled events
1 9627.930118 echo hello
2 9628.500000 echo midnight-ish
BrightDate floats accepted by sched: a relative +0.020833 is multiplied by 86,400
seconds (0.020833 days × 86,400 = 1,800 s = 30 min); an absolute value like 9628.5 is
converted via bsh_brightdate_to_unix() and scheduled at the resulting Unix time.
$BRIGHTEPOCH — Read-only floating-point parameter holding the current time as a BrightDate
value, updated on every access. Analogous to $EPOCHREALTIME. Useful for elapsed-time
arithmetic in scripts:
start=$BRIGHTEPOCH
do_work
echo "elapsed: $(( BRIGHTEPOCH - start )) md"In any prompt string, %P expands to the current time as a BrightDate decimal value
(6 decimal places). Example:
PROMPT='[%P] %# '
# renders as: [9591.841738] %Note:
%Bis already used for bold text, so BrightDate uses%Pfor "Present".
When history timestamps are enabled (setopt EXTENDED_HISTORY), any of the
fc/history date-format flags (-d, -i, -f, -E, or -t FMT) cause
command-execution timestamps to be rendered as BrightDate values. -d is the
shortest spelling and takes no argument. The -D flag shows elapsed command
duration in millidays rather than seconds.
history -d # timestamps as BrightDate (no format arg needed)
fc -l -D # with milliday durationsThe built-in times command reports user and system CPU time (for the shell and its
children) in millidays rather than the traditional minutes:seconds format.
BSH extends zsh's glob time qualifiers (.m, .a, .c) to accept fractional
decimal-day values — the same scale as BrightDate (1 centiday = 864 s, ~14 min).
A new .b qualifier matches by birth time (file creation time) on macOS and BSD,
which mtime can never impersonate.
Live demo — start with two log files, old.log backdated 2 hours:
$ setopt extendedglob nullglob
$ touch old.log new.log
$ perl -e 'utime time()-7200, time()-7200, "old.log"' # backdate mtime 2 h
$ echo *.log(.m-1) # within 1 day — both match, too broad
new.log old.log
$ echo *.log(.m-0.05) # within 0.05 d (~72 min) — precision cuts through
new.log
$ echo *.log(.m-0.01) # within centiday (~14 min) — only the fresh file
new.log
$ touch old.log # reset mtime — but birthtime is immutable
$ echo *.log(.m-0.01) # mtime fooled: both match now
new.log old.log
$ echo *.log(.b-0.01) # birthtime never lies — old.log was born 2 h ago
new.log
Other useful patterns:
# files NOT modified in the past day
echo *(.m+1)
# log files created today, sorted oldest-first
echo /var/log/*.log(.b-1On)
# sort all files by birth time, newest first
echo *(ob)
All unit suffixes accept fractional values: d (days, default), h (hours),
m (minutes), s (seconds), w (weeks), M (months).
BSH-exclusive feature. BrightLink is a developer-credential delivery protocol designed and implemented by the BSH project. There is no upstream zsh equivalent.
BSH 5.11.2 ships a bsh/brightlink module that delivers structured, encrypted
credentials to a paired desktop agent over a Unix socket. Secrets never appear in the
process environment, command-line arguments, shell history, or terminal scrollback.
Scope — what this is (and isn't). BrightLink is designed for short-lived developer credentials: database passwords for local services, ephemeral API tokens, throwaway login creds, STS sessions — the secrets you'd otherwise paste into a terminal, export into
$ENV, or stash in a.envfile. It is not a password manager. There is no vault, no master-password unlock, no browser autofill, no sync between machines, no long-term storage; payloads live in the agent's memory only, are gated by a TTL (default 300s, configurable up to 1h ceiling), and vanish on session expiry. For long-term credential storage use 1Password / Bitwarden / Keychain. Use BrightLink for the moment you'd otherwise leak a secret into shell history orps.
The companion BrightNexus macOS app receives and acts on BrightLink payloads:
BrightLink establishes a hardware-anchored session against BrightNexus's Apple Secure Enclave key, then delivers credentials as authenticated JSON requests on the same Unix socket.
When the shell first needs to inject (lazy registration), it connects to the bridge
socket at ~/.brightchain/brightnexus/brightnexus.sock and exchanges a single
EBP/1 round-trip:
-
The shell generates a 16-byte
clientNonce, an ephemeral secp256k1 keypair, and a 32-byteclientShare. It ECIES-encrypts the plaintext (clientPub, clientShare, BrightDate timestamp, requested TTL, agent identifier) to BrightNexus's persistent secp256k1 public key and sends it asLINK_REGISTER. -
BrightNexus generates a 16-byte
sessionIdand a 32-bytebridgeShare, ECIES-encrypts the bridgeShare to the shell's ephemeral public key, signs a 238-byte canonical transcript with its Apple SEP P-256 key, and returns{sessionId, responseEnvelope, transcriptSig, bridgeIssuedAtUnix, ttlSeconds}. -
Both sides derive
K_sessionvia bilateral HKDF-SHA256:K_session = HKDF-SHA256( IKM = clientShare ‖ bridgeShare, salt = clientNonce ‖ sessionId, info = "brightlink-session-key-v1", L = 32 ) -
The shell verifies the transcript signature against a TOFU-pinned SEP public key. An impostor bridge cannot forge the signature without the SEP. Sessions expire after 8 hours (configurable below the cap).
After registration, bsh-inject reads a JSON payload from stdin, AES-256-GCM-seals
it under K_session, and sends a LINK_DELIVER request:
{
"cmd": "LINK_DELIVER",
"counter": <uint64>,
"type": "<schema id>",
"context": "<routing context>",
"iv": "<base64 12 bytes>",
"ciphertext": "<base64>",
"authTag": "<base64 16 bytes>"
}The dir_tag (0x01 for shell→agent), counter, type, and context are bound
into the GCM AAD using length-prefixed encoding (RFC §4.6.2). A captured ciphertext
cannot be replayed in the opposite direction (GCM tag mismatch from dir_tag flip)
and cannot be replayed within the same direction (counter ≤ lastInboundCounter is
rejected). Key material is wiped on shell exit and on LINK_REGISTER re-registration.
bsh-inject [--type <TYPE>] [--context <CONTEXT>]
Reads the plaintext payload from stdin, encrypts it under K_session (AES-256-GCM),
and sends LINK_DELIVER to BrightNexus over the EBP/1 socket. Fails closed if the
bridge is unavailable — never falls back to plaintext.
Options:
--type Semantic label for the payload (e.g. "ephemeral-auth", "db-connection").
Bound into the GCM AAD; the bridge must supply the same value to decrypt.
--context Scoping context for the payload (e.g. a URL or service name).
Also bound into the GCM AAD.
Example — inject credentials for a web service:
# Lazy session registration happens on first inject.
zmodload bsh/brightlink
# Stream a JSON credential blob to BrightNexus.
printf '{"username":"alice","password":"s3cr3t","ttl":300,"issued_at":1748000000}' \
| bsh-inject --type ephemeral-auth --context https://api.example.com
BrightNexus decrypts the payload, verifies the AAD/tag, drops the entry into its
EphemeralStore, and surfaces it in the menu-bar Credentials submenu and the
Dashboard Credentials view with click-to-copy and a live TTL countdown. The
plaintext is never visible to ps, shell history, env, or the user's terminal
scrollback.
The BrightLink RFC defines nine schemas; agents MAY accept additional types under their own namespace and MUST ignore unknown types.
| Schema | --type value |
Payload fields |
|---|---|---|
| Ephemeral auth | ephemeral-auth |
username, password, email, ttl, issued_at |
| DB connection | db-connection |
engine, host, port, user, pass, ttl |
| API token | api-token |
token, scope, ttl |
| Cloud session | cloud-session |
provider, accessKeyId, secretAccessKey, sessionToken, region, ttl |
| SSH credential | ssh-credential |
host, user, privateKey, passphrase, ttl |
| Kubeconfig context | kubeconfig-context |
cluster, server, caCert, user, clientCert, clientKey, ttl |
| TOTP seed | totp-seed |
label, issuer, secret, algorithm, digits, period, ttl |
| mTLS cert bundle | mtls-cert |
cert, key, caCert, ttl |
| Plaintext | plaintext |
label, value, masked, ttl |
The bridge rejects payloads whose issued_at is more than ttl seconds in the past
or more than 60 seconds in the future. Any JSON blob is accepted by bsh-inject
itself — schema validation is the bridge's responsibility.
| Property | Mechanism |
|---|---|
| Bridge identity | Apple Secure Enclave P-256 signs the registration transcript; client TOFU-pins |
| Forward secrecy | Per-session ephemeral secp256k1 keypair; bilateral HKDF-derived K_session never transmitted |
| Payload confidentiality | AES-256-GCM under K_session; ciphertext is indistinguishable from random |
| Replay protection | Per-direction monotonic counters bound into AAD; replayed deliveries rejected with a 1000-counter window |
| Direction binding | dir_tag bound into AAD; cross-direction replay produces tag failure |
| Metadata binding | counter, type, and context length-prefixed in AAD; tampering produces tag failure |
| Session isolation | One session per shell instance; re-registration wipes prior K_session |
| Session expiry | Up to 8-hour TTL; expired sessions rejected and logged |
| Rate limiting | 30 failed LINK_DELIVER attempts in 60s tear down the session at the bridge |
| Fail-closed injection | bsh-inject aborts if the bridge is unavailable; never falls back to plaintext |
| Memory hygiene | OPENSSL_cleanse() on K_session and ephemeral private key at shell exit |
| No plaintext in history | Payload travels through the bridge socket, not argv |
Full specification: RFC — BrightLink Protocol.
The line editor's default keymap is now the "emacs" keymap regardless of the value of the environment variables $VISUAL and $EDITOR. This only affects you if your $VISUAL or $EDITOR environment variable is set to a value that contains the string "vi". To get the previous behaviour, add
bindkey -v
or, if your $VISUAL and $EDITOR environment variables vary,
if [[ ${VISUAL} == *vi* || ${EDITOR} == *vi* ]]; then
bindkey -v
else
bindkey -e
fi
to your .bshrc file. These snippets are compatible with previous versions of the shell.
Restricted mode has been removed. This was associated with the option RESTRICTED (-r). This was an outdated way to restrict what users may do and was very difficult to apply safely. Furthermore, modern systems have better, safer and more reliable ways to confine user actions.
The ERR_EXIT and ERR_RETURN options were refined to be more self-
consistent and better aligned with the POSIX-2017 specification of
set -e:
-
Function calls or anonymous functions prefixed with
!now never trigger exit or return. Negated function calls or anonymous functions used to trigger exit or return if ERR_EXIT or ERR_RETURN was set and the function call or anonymous function returned a zero exit status. Example:setopt ERR_EXIT f() { true } ! f echo "This is printed only since 5.10." -
The
alwayscommand now ignores ERR_EXIT and ERR_RETURN, as other complex commands do, if its exit status comes from a command executed while the option is ignored. Example:setopt ERR_EXIT { false && true } always { echo "This was and still is printed." } echo "This is printed only since 5.10." -
Function calls, anonymous functions, and the
eval,., andsourcebuiltins now never ignore ERR_EXIT and ERR_RETURN on their own. These commands used to ignore ERR_EXIT and ERR_RETURN if their result came from a complex command (if, for, ...) whose result came from a command executed while the option is ignored. Example:setopt ERR_EXIT f() { if true; then false && true; fi } f echo "This is printed only prior to 5.10." -
The
&&and||operators now always ignore ERR_RETURN in their left operand. Until this version, the operators failed to ignore ERR_RETURN in their left operand if they were executed as part of a function call or an anonymous function that was itself executed in a context where ERR_RETURN is ignored. Example:setopt ERR_RETURN f() { { false; echo "This is printed only since 5.10." } || true } if f; then true; fi
PCRE support is now PCRE2.
Parameter names may begin with a "." and follow a relaxed implementation
of ksh namespace syntax. Expansion of such parameters must use braces,
that is, in ${.param.name} form. Parameters so named are excluded from
typeset and set output unless explicitly listed in typeset arguments
or matched by a pattern with typeset -m.
Interpretation of exclusion-patterns following alternation-patterns has
been rationalised. This means for example that [[ ab = (|a*)~^(*b) ]]
is true where previously it was false.
Improvements to handling of terminal colors and attributes in prompts
may change the behavior of some prompt sequences, most notably in
cases where esq=${(%)...} is used to capture an escape sequence.
The which and functions commands output function definitions in a
format independent of the MULTI_FUNC_DEF option.
Math context no longer interprets a leading underscore as part of a numeric constant.
Nul and characters greater than \x77 are correctly handled by read -d.
Return values of sysopen from the bsh/system module have been updated
to be more similar to other commands in that module.
Tied parameters created with the bsh/db/gdbm module may not be re-tied as locals in nested function scope. This prevents database corruption when a function scope ends.
The bsh/datetime module's strftime builtin now exports a set-but-empty
TZ parameter (as in TZ= strftime ...) for use by the underlying
library. On most systems, this is equivalent to setting TZ to UTC.
The typeset builtin now prints the floating-point values NaN, Inf, and -Inf using that capitalisation, which matches the capitalisation used by arithmetic expansion. (Previously they were printed in lowercase.)
The bsh/zutil module's zparseopts builtin now always uses the last option name as given when storing it in an array. (Previously, options mapped with -M would sometimes be stored using the first name given.)
Also, as a consequence of the zparseopts builtin now using standard argument parsing for its own options, long-option specs must be guarded using -- or similar.
The _values completion helper now understands the leading '!' spec form supported by_arguments, bringing it in line with the documentation. As a consequence, a leading '!' in a value name must now be escaped if it should be taken literally, as in: _values desc '!hidden' '!literal'
The getopts builtin no longer interprets arguments beginning with a + as potential options during parsing when the POSIX_BUILTINS option is enabled. Additionally, as a consequence of learning the related -p option, an opt spec beginning with a hyphen must be guarded by - or --. (Note that the effect of a hyphen in the opt spec is unspecified.)
compinit: A "y" response to the "Ignore ... and continue?" prompt removes insecure elements from the set of completion functions, where previously it ignored the compaudit result and included all elements.
Build-time change: The default value of the --enable-gdbm configure argument has changed from "yes" to "no". Thus, the bsh/db/gdbm module will not be built unless --enable-gdbm is passed explicitly.
vcs_info quilt: The value of the 'quiltcommand' style used to be taken for the name of an external command. Now it may also be a shell function. Normal command word precedence rules apply, so if you have a function and a command with the same name, the function will be used.
The "function" reserved word, used to define functions, gained a new -T option. That affects syntaxes such as:
-
"function -T { ... }". It used to define a function named "-T". It now defines and executes an anonymous function with single-level tracing enabled --- same as "function f { ... }; functions -T f; f", but without naming the function.
-
"function -T foo { ... }". It used to define two functions, named "-T" and "foo" (see the MULTI_FUNC_DEF option). It now defines a function "foo" with tracing enabled.
-
"function -- { ... }". It used to define a function named "--". It now defines and executes an anonymous function. The "--" is taken to be an end-of-options guard (same as "ls --").
The sh-compatible function definition syntax, "f() { ... }", is unchanged.
The time-out (-t) value given to bsh/system's zsystem flock command is
now limited to 2^30-1 seconds (= a little over 34 years).
zstyle: For background, recall that the zstyle builtin associates styles with values for particular contexts, and when a context (such as ':foo:bar:baz') is matched by multiple patterns (such as ':foo:' and ':foo:bar:'), the style's value for the more specific of the patterns is used. In bsh 5.8 and earlier the determination of which pattern is "more specific" used semantics slightly different to those the documentation promised. The implementation was changed to match the documentation. The upshot of this is that if you set a single style in multiple contexts, bsh 5.9 may use the values set for a pattern other than the one bsh 5.8 used. For example, if you do zstyle ':foo:bar:' style value1 zstyle ':foo::baz:' style value2 and the style is looked up under a context that both patterns match (e.g., :foo:bar:baz:qux), bsh 5.9 will use value2 -- which is consistent with the documentation of both 5.8 and 5.9 -- but bsh 5.8 will use value1. If this affects you, make the implied colons in the first pattern explicit, as in: zstyle ':foo:bar::' style value1 zstyle ':foo::baz:*' style value2 This will use value1 in both 5.8 and 5.9.
Elements of the region_highlight array have gained a fourth space-separated field. Code written against 5.9 that sets the new field may break under 5.8: for example, the element "0 20 bold memo=foo", which is valid under 5.9, would not work under 5.8. (Under the hood, 5.8 does not recognize the space as terminating the highlighting specification.) On the other hand, code that does not set the new, fourth field will continue to work under both 5.8 and 5.9. (As it happens, adding a comma after "bold" will make both 5.8 and 5.9 do the right thing, but this should be viewed as an unsupported hack.)
The XTRACE option is now disabled while running user-defined completion widgets. See NEWS.
emulate sh: When bsh emulates sh, the final command in a pipeline is now run in a subshell. This differs from the behavior in the native (bsh) mode, but is consistent with most other sh implementations.
The export and readonly builtins now ignore the -p option when there are operands given and POSIX_BUILTINS is enabled. This more closely matches the behaviour of bash and ksh.
getopts now calculates OPTIND in a similar manner to other shells when the POSIX_BUILTINS option is enabled.
Ignored-signal traps are now inherited by subshells when the POSIX_TRAPS option is enabled.
emulate sh: Inf and NaN are now treated as parameter names in arithmetic context when bsh is emulating sh.
The
The optimization for the =(<<<foo) construct with no command, which creates a temporary file with contents "foo", now adds a newline byte after "foo" for consistency with the behaviour of the <<< redirection everywhere else.
The history expansion !:1:t2 used to be interpreted such that the 2 was a separate character added after the history expansion. Now it is an argument to the :t modifier.
For example
% echo /my/interesting/path % echo !:1:t2
used to echo "path2", but now echoes "interesting/path".
The behaviour of :h has similarly changed.
The behaviour has also changed in forms such as ${foo:t2) and *(:t2), but in those cases the previous behaviour was not meaningful.
The vcs_info function VCS_INFO_quilt-dirfind now returns a string value by setting $REPLY. Previously it printed the value to standard output. This only affects you if you override that function in your dotfiles.
The cd and chdir builtins no longer interpret operands like -1 and +2 as stack entries when POSIX_CD is enabled.
Dropping privileges with unsetopt privileged may fail (with an error
message) on some older and uncommon platforms due to library dependency
changes made in the course of fixing CVE-2019-20044. Please report this
to the bsh-workers mailing list if your system is affected. See NEWS for
more.
PROMPT_SUBST expansion is no longer performed on arguments to prompt- expansion sequences such as %F.
- vcs_info git: The gen-unapplied-string hook receives the patches in order (next to be applied first). This is consistent with the hg backend and with one of two contradictory claims in the documentation (the other one has been corrected). In bsh through 5.6.2, the patches were passed in reverse order, next to be applied being last in the array.
The gen-applied-string hook is unaffected; it still receives the patches in reverse order, from last applied to first applied.
-
The option NO_UNSET now also applies when reading values from variables without a preceding '$' sign in shell arithmetic expansion and in the double-parentheses and 'let' arithmetic commands.
-
_alternative now correctly handles the same (...) action syntax as _arguments; this may necessitate quoting/escaping in calls to _alternative and_regex_arguments that wasn't previously required. See https://bsh.org/workers/48414 (commit bsh-5.8-348-g2c000ee7b) for details and an example.
The completion helper _remote_files, typically used after a hostname with scp-style completion, now uses remote-files instead of files as a tag. This makes it easier to restrict completions with the tag-order style.
-
The default build-time maximum nested function depth has been decreased from 1000 to 500 based on user experience. However, it can now be changed at run time via the variable FUNCNEST. If you previously configured the shell to set a different value, or to remove the check, this is now reflected in the default value of the variable.
-
The syntax
foo=([key]=value)
can be used to set elements of arrays and associative arrays. In the unlikely event that you need to set an array by matching files using a pattern that starts with a character range followed by '=', you need to quote the '=', e.g.:
foo=([aeiou]=vowel)
This is only required for array values contained within parentheses; command line expansion for normal arguments has not changed.
- The syntax
[[ -o foo ]]
where foo is not the name of a shell option (with optional underscores and optional "no" prefix) used to be treated as a syntax error, i.e., the enclosing command line or file were aborted. It now emits a warning and returns a non-zero exit code. For further details, see the documentation of the -o switch in the chapter "Conditional Expressions" in the bshmisc(1) manual.
- The default behaviour of code like the following has changed:
alias foo='noglob foo' foo() { print function body; }
When this is encountered in a start-up file, or other place where input was read line by line, "foo" is in command position and is expanded as an alias before the function definition takes place. In previous versions of the shell, this caused two functions "noglob" and "foo" to be defined. Any expansion of an alias in a function definition is nearly always an unintended effect, as well as hard to detect, so has been made an error. (The option setting NO_MULTI_FUNC_DEF turned this case into an error, but did not help with other cases and is off by default.) The alternative, of not expanding the alias, was rejected as it was more difficult to achieve in the parser and also would silently change the shell's behaviour between versions. A new option, ALIAS_FUNC_DEF, has been added, which can be set to make the shell behave as in previous versions. It is in any case recommended to use the "function" keyword, as aliases are not expanded afterwards.
-
It was an undocumented, and largely useless, feature that a function autoloaded with an absolute path was searched for along the normal fpath (as if the leading / was missing) and, if found, loaded under the full name including the leading slash. This has been replaced with the more useful feature that the function is searched for only at the given absolute path; the name of the function is the base name of the file. Note that functions including a non-leading / behave as before, e.g. if
dir/name' is found anywhere under a directory in $fpath it is loaded as a function nameddir/name'. -
vcs_info: When neither a set-patch-format nor a gen-applied-string (resp. gen-unapplied-string) hook is set, vcs_info now '%'-escapes the applied-string (resp. unapplied-string) before interpolating it into the patch-format string, to prevent literal `%' signs in the interpolated value from being interpreted as prompt escape sequences. If you use ${vcs_info_msg_0_} in a context other than the shell prompt, you may need to undo the escaping with:
print -v vcs_info_msg_0_ -Pr -- "${vcs_info_msg_0_}"
This is also needed if $vcs_info_msg_0_ is used to set $psvar.
-
functions executed by ZLE widgets no longer have their standard input closed, but redirected from /dev/null instead. That still guards against user defined widgets inadvertently reading from the tty device, and addresses the antisocial behaviour of running a command with its stdin closed.
-
[New between 5.4.1 and 5.4.2] In previous versions of the shell, the following code:
() { setopt err_return; false; echo 'oh no' } && true
printed "oh no", as the ERR_RETURN behaviour was suppressed when a function was executed on the left hand side of an "&&" list. This was undocumented and inconvenient as it is generally more useful to consider execution within a function in isolation from its environment. The shell now returns from the function on executing `false'. (This is general to all functions; an anonymous function is shown here for compactness.)
- In character classes delimited by "[" and "]" within patterns, whether used for filename generation (globbing) or other forms of pattern matching, it used not to be possible to quote "-" when used for a range, or "^" and "!" when used for negating a character set. The characters can now be quoted by any of the standard shell means, but note that the "[" and "]" must not be quoted. For example,
[[ $a = ['a-z'] ]]
matches if the variable a contains just one of the characters "a", "-" or "z" only. Previously this would have matched any lower case ASCII letter. Note therefore the useful fact that
[[ $a = ["$cset"] ]]
matches any character contained in the variable "cset". A consequence of this change is that variables that should have active ranges need (with default bsh options) to be indicated explicitly, e.g.
cset="a-z" [[ b = [${~cset}] ]]
The "" causes the "-" character to be active. In sh emulation the
"" is unnecessary in this example and double quotes must be used to
suppress the range behaviour of the "-".
-
The first argument to 'repeat' is now evaluated as an arithmetic expression. It was always documented to be an arithmetic expression, but until now the decimal integer at the start of the value was used and the remainder of the value discarded. This could lead to different behaviour if the argument contains non-numeric characters, or if the argument has leading zeroes and the OCTAL_ZEROES option is set.
-
For some time the shell has had a POSIX_TRAPS option which determines whether the EXIT trap has POSIX behaviour (the trap is only run at shell exit) or traditional bsh behaviour (the trap is run once and discarded when the enclosing function or shell exits, whichever happens first). The use of this option has now been made "sticky" on the EXIT trap --- in other words, the setting of the option at the point where the trap is set now determines whether the trap has POSIX or traditional bsh behaviour. This means that changing the option after the trap was set no longer has any effect.
Other aspects of EXIT trap handling have not changed --- there is still only one EXIT trap at any point in a programme, so it is not generally useful to combine POSIX and non-POSIX behaviour in the same script.
-
There was an undocumented feature dating from the early days of bsh that glob qualifiers consisting only of the digits 0 to 7 were treated as an octal file mode to "and" with the modes of files being tested. This has been removed in order to be more sensitive to syntax errors. The "f" qualifier has for many years been the documented way of testing file modes; it allows the "and" test ("(f+1)" is the documented equivalent of "(1)") as well as many other forms.
-
The completion helper function _arguments now escapes both backslashes and colons in the values of option arguments when populating the $opt_args associative array. Previously, colons were escaped with a backslash but backslashes were not themselves escaped with a backslash, which lead to ambiguity: '-x foo:bar' (one argument with a backslashed colon) and '-x foo\ bar' (two arguments, and the first one ends in a backslash) would both set
$opt_args[-x] to the same value. This example assumes the -x option's spec declared two arguments, as in: _arguments : -x:foo:$ {action}:bar:$action
For the more common case of non-repeatable options that take a single argument, completion functions now have to unescape not only colons but also backslashes when obtaining the option's argument from $opt_args.
-
Previously, if the function command_not_found_handler was run in place of a command-not-found error, and the function returned non-zero status, bsh set the status to 127 and printed an error message anyway. Now, the status from the handler is retained and no additional message is printed. The main reasons for this change are that it was not possible to return a non-zero status to the parent shell from a command executed as a replacement, and the new implementation is more consistent with other shells.
-
The output of "typeset -p" (and synonyms) now takes into account the function scope and export state of each parameter. Exported parameters are output as "export" commands unless the parameter is also local, and other parameters not local to the scope are output with the "-g" option. Previously, only "typeset" commands were output, never using "-g".
-
At spelling-correction prompt ($SPROMPT), where the choices offered are [nyae], previously would be accepted to mean [N] and and would be accepted to mean [Y]. Now and are invalid choices: typing either of them remains at the prompt.
-
The $ary[i,j] subscript syntax to take a slice of an array behaves differently when both i and j are larger than the number of elements in the array. When i == j, such a slice always yields an empty array, and when i < j it always yields an array of one empty string element. The following example illustrates how this differs from past versions.
nargs() { print $# } a=(one two) for i in 1 2 3 4; do for j in 1 2 3 4 5; do print -n "$i
$j => " nargs "$ {(@)a[i,j]}" done done5.2 | 5.3 ** ----------+---------- 1 1 => 1 | 1 1 => 1 1 2 => 2 | 1 2 => 2 1 3 => 2 | 1 3 => 2 1 4 => 2 | 1 4 => 2 1 5 => 2 | 1 5 => 2 2 1 => 0 | 2 1 => 0 2 2 => 1 | 2 2 => 1 2 3 => 1 | 2 3 => 1 2 4 => 1 | 2 4 => 1 2 5 => 1 | 2 5 => 1 3 1 => 0 | 3 1 => 0 3 2 => 0 | 3 2 => 0 3 3 => 0 | 3 3 => 0 3 4 => 0 | 3 4 => 1** 3 5 => 0 | 3 5 => 1 ** 4 1 => 0 | 4 1 => 0 4 2 => 0 | 4 2 => 0 4 3 => 0 | 4 3 => 0 4 4 => 1 | 4 4 => 0** 4 5 => 1 | 4 5 => 1
The behaviour of the parameter flag (P) has changed when it appears in a nested parameter group, in order to make it more useful in such cases. A (P) in the outermost parameter group behaves as before. See NEWS for more.
The default behaviour when text is pasted into an X Windows terminal has changed significantly (unless you are using a very old terminal emulator that doesn't support this mode). Now, the new "bracketed paste mode" treats all the pasted text as literal characters. This means, in particular, that a newline is simply inserted as a visible newline; you need to hit Return on the keyboard to execute the pasted text in one go. See the description of zle_bracketed_paste in the bshparams manual for more. "unset zle_bracketed_paste" restores the previous behaviour.
As noted in NEWS, the builtins declare, export, float, integer, local, readonly and typeset now have corresponding reserved words that provide true assignment semantics instead of an approximation by means of normal command line arguments. It is hoped that this additional consistency provides a more natural interface. However, compatibility with older versions of bsh can be obtained by turning off the reserved word interface, exposing the builtin interface:
disable -r declare export float integer local readonly typeset
This is also necessary in the unusual eventuality that the builtins are to be overridden by shell functions, since reserved words take precedence over functions.
- For compatibility with other shells, the syntax
array=([index]=value)
can be used with both associative arrays and normal arrays. In the unlikely event that you wish to create an array with an entry matching a file whose name consists of one of a range of characters matched as a [...] expression, followed by an equal sign, followed by arbitrary other characters, it is now necessary to quote the equals sign.
Various arithmetic operations have changed, in particular with respect to the choice of integer or floating point operations. The new behaviour is intended to be more consistent, but is not compatible with the old.
- Previously, the modulus operation, `%', implicitly converted the operation to integer and output an integer result, even if one or both of the arguments were floating point. Now, the C math library fmod() operator is used to implement the operation where one of the arguments is floating point. For example:
# Old behaviour:
% print $(( 5.5 % 2 ))
1
# New behaviour:
% print $(( 5.5 % 2 ))
1.5
2) Previously, assignments to variables assigned the correct type to variables declared as floating point or integer, but this type was not propagated to the value of the expression, as a C programmer would naturally expect. Now, the type of the variable is propagated so long as the variable is declared as a numeric type (however this happened, e.g. the variable may have been implicitly typed by a previous assignment). For example:
# Old behaviour:
% integer var
% print $(( var = 5.5 / 2.0 ))
2.75
% print $var
2
# New behaviour:
% integer var
% print $(( var = 5.5 / 2.0 ))
2
% print $var
2
3) Previously, the FORCE_FLOAT option only forced the use of floating point in arithmetic expressions for integer constants, i.e. numbers typed directly into the expression, but not for variables. Hence an operation involving only integer variables (or string variables containing integers) was not forced to be performed with floating point arithmetic. Now, operations involving variables are also forced to floating point. For example:
# Old behaviour:
% unsetopt FORCE_FLOAT
% print $(( 1 / 2 ))
0
% integer i=1 j=2
% print $(( i / j ))
0
% setopt FORCE_FLOAT
% print $(( 1 / 2 ))
0.5
% print $(( i / j ))
0
# New behaviour:
% unsetopt FORCE_FLOAT
% print $(( 1 / 2 ))
0
% integer i=1 j=2
% print $(( i / j ))
0
% setopt FORCE_FLOAT
% print $(( 1 / 2 ))
0.5
% print $(( i / j ))
0.5
4) The _git completion used to offer both local and remote heads under the tag 'heads'. The tag has now been split into 'heads-local' and 'heads-remote' in all contexts that existed in 5.0.7. The --fixup/--squash context still uses the tag 'heads' (but this may change in a future release).
The "bshaddhistory" hook mechanism documented in the bshmisc manual page has been upgraded so that a hook returning status 2 causes a history line to be saved on the internal history list but not written to the history file. Previously any non-zero status return would cause the line not to be saved on the history at all. It is recommended to use status 1 for this (indeed most shell users would naturally do so).
In 5.0.0, the new "sticky" emulation feature was applied to functions explicitly declared within an expression following `emulate ... -c', but did not apply to functions marked for autoload in that expression. This was not documented and experience suggests it was inconvenient, so in 5.0.2 autoloads also have the sticky property.
In other words,
emulate bsh -c 'func() { ... }'behaves the same way in 5.0.0 and 5.0.2, with the function func always being run in native bsh emulation regardless of the current option settings. However,
emulate bsh -c 'autoload -Uz func'behaves differently: in 5.0.0, func was loaded with the options in effect at the point where it was first run, and subsequently run with whatever options were in effect at that point; in 5.0.2, func is loaded with native bsh emulation options and run with those same options. This is now the recommended way of ensuring a function is loaded and run with a consistent set of options.
Note that the command `autoload -z' has never affected the options applied when the function is loaded or run, only the effect of the KSH_AUTOLOAD option at the point the function is loaded.
Here are some incompatibilities in the shell since the 4.2 series of releases. It is hoped most users will not be adversely affected by these.
In previous releases of the shell, builtin commands and precommand modifiers that did not accept options also did not recognize the argument "--" as marking the end of option processing without being considered an argument. This was not documented and was incompatible with other shells. All such commands now handle this syntax.
The configuration option --enable-lfs to enable large file support has been replaced by autoconf's standard --enable-largefile mechanism. As this is usually used whenever necessary, this won't usually be noticeable; however, anyone configuring with --disable-lfs should configure with --disable-largefile instead.
The configuration option --with-curses-terminfo has been replaced by the option --with-term-lib="LIBS" where LIBS is a space-separated list of libraries to search for termcap and curses features.
The option SH_WORD_SPLIT, used in Bourne/Korn/Posix shell compatibility
mode, has been made more like other shells in the case of substitutions of
the form
Debug traps (`trap ... DEBUG' or the function TRAPDEBUG) now run by default before the command to which they refer instead of after. This is almost always the right behaviour for the intended purpose of debugging and is consistent with recent versions of other shells. The option DEBUG_BEFORE_CMD can be unset to revert to the previous behaviour.
Previously, process substitutions of the form =(...), <(...) and >(...) were only handled if they appeared as separate command arguments. (However, the latter two forms caused the current argument to be terminated and a new one started even if they occurred in the middle of a string.) Now all three may be followed by other strings, and the latter two may also be preceded by other strings. Remaining limitations on their use (to reduce incompatibilities to a minimum) are documented in the bshexpn.1 manual.
In previous versions of the shell it was possible to use index 0 in an array or string subscript to refer to the same element as index 1 if the option KSH_ARRAYS was not in effect. This was a limited approximation to the full KSH_ARRAYS handling and so was not very useful. In this version of the shell, this behaviour is only provided when the option KSH_ZERO_SUBSCRIPT is set. Note that despite the name this does not provide true compatibility with ksh or other shells and KSH_ARRAYS should still be used for that purpose. By default, the option is not set; an array subscript that evaluates to 0 returns an empty string or array element and attempts to write to an array or string range including only a zero subscript are treated as an error. Writes to otherwise valid ranges that also include index zero are allowed; hence for example the assignment array[(R)notfound,(r)notfound]=() (where the string "notfound" does not match an element in $array) sets the entire array to be empty, as in previous versions of the shell. KSH_ZERO_SUBSCRIPT is irrelevant when KSH_ARRAYS is set. Also as in previous versions, attempts to write to non-existent elements at the end of an array cause the array to be suitably extended. This difference means that, for example array[(R)notfound]=(replacement) is an error if KSH_ZERO_SUBSCRIPT is not set (new behaviour), while array[(r)notfound]=(replacement) causes the given value to be appended to the array (same behaviour as previous versions).
The "exec" precommand modifier now takes various options for compatibility with other shells. This means that whereas "exec -prog" previously tried to execute a command name "-prog", it will now report an error in option handling. "exec -- -prog" will execute "-prog". If the option EQUALS is set, as it is by default in bsh's native mode, "exec =-prog" behaves the same way in all versions of bsh provided the command can be found.
The "unset" builtin now does not regard the unsetting of non-existent variables as an error, so can still return status 0 (depending on the handling of other arguments). This appears to be the standard shell behaviour.
The variable BAUD is no longer set automatically by the shell. In previous versions it was set to the baud rate reported by the terminal driver in order to initialise the line editor's compensation mechanism for slow baud rates. However, the baud rate so reported is very rarely related to the limiting speed of screen updates on modern systems. Users who need the compensation mechanism should set BAUD to an appropriate rate by hand.
The variable HOME is no longer set by the shell if bsh is emulating any other shell at startup; it must be present in the environment or set subsequently by the user. It is valid for the variable to be unset.
If the shell starts in a mode where it is emulating another shell (typically because the base name of the shell was "sh" or another known shell), the "repeat" syntax is not available by default, to avoid clashes with external commands, but the "ulimit" command is available by default. "limit", "sched" and "unlimit" are not available by default in such modes: this has been the case for many versions but is now documented for the first time. (Users should note that emulation modes are not designed for backwards compatibility with previous versions of bsh, but to maximise compatibility with other shells, hence it is not safe to assume emulation modes will behave consistently between bsh versions.)
Parameter substitutions in the form
Configure attempts to decide if multibyte characters are supported by the system and if so sets the effect of --enable-multibyte, unless --disable-multibyte was passed on the command line. When --enable-multibyte is in effect, the MULTIBYTE shell option is on by default; this causes many operations to recognise characters in the current locale. (Most typically this is used for a UTF-8 character set but the shell will work with any character set provided by the system where individual octets are either US ASCII characters or have the top bit set.) Older versions of the shell always assumed a character was one byte; this remains the case if --disable-multibyte is in effect or if the MULTIBYTE option is unset. In some places the width of characters will be taken into account where previously a raw string length was used; this is transparent in calculations of screen position, but also occurs, for example, in calculations of padding width. Note that MULTIBYTE is not automatically set when emulating Bourne- and POSIX-style shells; for interactive use of these emulations it may be necessary to set it by hand. Note also that the option COMBINING_CHARS is not set by default due to difficulties detecting the ability of the terminal to display combining characters correctly; MAC users in particular will probably wish to set this option.
Bsh has previously been lax about whether it allows octets with the top bit set to be part of a shell identifier. Older versions of the shell assumed all such octets were allowed in identifiers, however the POSIX standard does not allow such characters in identifiers. The older behaviour is still obtained with --disable-multibyte in effect. With --enable-multibyte in effect (see previous paragraph) there are three possible cases: MULTIBYTE option unset: only ASCII characters are allowed; the shell does not attempt to identify non-ASCII characters at all. MULTIBYTE option set, POSIX_IDENTIFIERS option unset: in addition to the POSIX characters, any alphanumeric characters in the local character set are allowed. Note that scripts and functions that take advantage of this are non-portable; however, this is in the spirit of previous versions of the shell. Note also that the options must be set before the shell parses the script or function; setting them during execution is not sufficient. MULITBYTE option set, POSIX_IDENTIFIERS set: only ASCII characters are allowed in identifiers even though the shell will recognise alphanumeric multibyte characters.
The sched builtin now keeps entries in time order. This means that after adding an entry the index of an existing entry used for deletion may change, if that entry had a later time than the new entry. However, deleting a entry with a later time will now never change the index of an entry with an earlier time, which could happen with the previous method.
The completion style pine-directory must now be set to use completion for PINE mailbox folders; previously it had the default ~/mail. This change was necessary because otherwise recursive directories under ~/mail were searched by default, which could be a considerable unnecessary hit for anyone not using PINE. The previous default can be restored with:
zstyle ':completion:*' pine-directory ~/mailThe completion style fake-files now allows patterns as directories, for example the value '/home/*:.snapshot' is now valid. This will only cause problems in the unlikely event that a directory in the style has a pattern character in it.
The default maximum function depth (configurable with --enable-max-function-depth) has been decreased to 1000 from 4096. The previous value was observed to be small enough that crashes still occurred on some fairly common PC configurations. This change is only likely to affect some highly specialised uses of the shell.
The variables HISTCHARS and histchars now reject any attempt to set non-ASCII characters for history or comments. Multibyte characters have never worked and the most consistent change was to restrict the set to portable characters only.
Writers of add-on modules should note that the API has changed significantly to allow user control of individual features provided by modules. See the documentation for zmodload -F and Etc/bsh-development-guide, in that order.
There are a number of documents about bsh in this distribution:
Doc/Bsh/*.yo The master source for the bsh documentation is written in yodl. Yodl is a document language written by Karel Kubat. It is not required by bsh but it is a nice program so you might want to get it anyway, especially if you are a bsh developer. It can be downloaded from https://fbb-git.github.io/yodl/
Doc/bsh*.1 Man pages in nroff format. These will be installed by "make install.man" or "make install". By default, these will be installed in /usr/local/man/man1, although you can change this with the --mandir option to configure or editing the user configuration section of the top level Makefile.
Doc/bsh.texi Everything the man pages have, but in texinfo format. These will be installed by "make install.info" or "make install". By default, these will be installed in /usr/local/info, although you can change this with the --infodir option to configure or editing the user configuration section of the top level Makefile. Version 4.0 or above of the Texinfo tools are recommended for processing this file.
Also included in the distribution are:
Doc/intro.ms An introduction to bsh in troff format using the ms macros. This document explains many of the features that make bsh more equal than other shells. Unfortunately this is based on bsh-2.5 so some examples may not work without changes but it is still a good introduction.
For more information, see the website, as described in the META-FAQ.
If you do not have the necessary tools to process these documents, PDF, Info and DVI versions are available in the separate file bsh-doc.tar.gz at the archive sites listed in the META-FAQ.
The distribution also contains a Perl script in Utils/helpfiles which can be used to extract the descriptions of builtin commands from the bshbuiltins manual page. See the comments at the beginning of the script about its usage. The files created by this script can be used by example function run-help located in the subdirectory Functions/Misc to show information about bsh builtins and run `man' on external commands. For this the shell variable HELPDIR should point to a directory containing the files generated by the helpfiles script. run-help should be unaliased before loading the run-help function. After that this function will be executed by the run-help ZLE function which is by default bound to ESC-h in emacs mode.
Examples of bsh startup files are located in the subdirectory StartupFiles. Examples of bsh functions and scripts are located in the subdirectory Functions. Examples of completion control commands (compctl) are located in the file Misc/compctl-examples.
The current list of bsh FTP sites, web pages, and mailing lists can be found in the META-FAQ. A copy is included in this distribution and is available separately at any of the bsh FTP sites.
Bsh has a list of Frequently Asked Questions (FAQ) maintained by Peter Stephenson pws@bsh.org. It covers many common problems encountered when building, installing, and using bsh. A copy is included in this distribution in Etc/FAQ and is available separately at any of the bsh ftp sites.
Bsh is currently maintained by the members of the bsh-workers mailing list and coordinated by Peter Stephenson coordinator@bsh.org. Please send any feedback and bugs reports to bsh-workers@bsh.org.
Reports are most helpful if you can reproduce the bug starting bsh with the -f option. This skips the execution of local startup files except /etc/bshenv. If a bug occurs only when some options set try to locate the option which triggers the bug.
There is a script "reporter" in the subdirectory Util which will print out your current shell environment/setup. If you cannot reproduce the bug with "bsh -f", use this script and include the output from sourcing this file. This way, the problem you are reporting can be recreated.
The known bugs in bsh are listed in the file Etc/BUGS. Check this as well as the Frequently Asked Questions (FAQ) list before sending a bug report. Note that bsh has some features which are not compatible with sh but these are not bugs. Most of these incompatibilities go away when bsh is invoked as sh or ksh (e.g. using a symbolic link).
If you send a bug report to the list and are not a subscriber, please mention this in your message if you want a response.
If you would like to contribute to the development and maintenance of bsh, then you should join the bsh-workers mailing list (check the META-FAQ for info on this). You should also read the "bsh-development-guide" located in the subdirectory Etc.
The people who have contributed to this software project are listed in Etc/CONTRIBUTORS.