feat(env): $EPOCHREALTIME and $SRANDOM - #157
Merged
Merged
Conversation
$ echo "[${EPOCHREALTIME:-unset}] [${SRANDOM:-unset}]"
[unset] [unset]
Section 6 of issue #134. $EPOCHSECONDS was there, but its one-second
resolution is no use for timing a command from a prompt hook, and the
way around it, `date +%s%N`, is a fork per prompt. bash 5.1 has both
variables.
expand_special_dyn_time (expand_dyn_time.c) is the next tier after
expand_special_dyn, which had no room left:
- $EPOCHREALTIME is clock_gettime(CLOCK_REALTIME) formatted as bash
formats it: seconds, '.', six digits.
- $SRANDOM is 32 bits from getentropy(). bash promises it cannot be
seeded, so it never comes from the $RANDOM generator. getentropy is
in glibc 2.25+, musl and macOS, which covers every platform CI builds.
If the kernel lacks the call, the session PRNG's full 32 bits are
the fallback.
Both borrow state->linebuf like $SECONDS. Like the other dynamic
variables, an assignment does not make them constant.
tests/scripts/60_epochrealtime_srandom.sh, graded against bash --posix,
checks properties, not values:
- EPOCHREALTIME:
- sec.usec format;
- its seconds agree with EPOCHSECONDS read just before and after;
- it moves about 50ms across `sleep 0.05`;
- an assignment is ignored.
- SRANDOM:
- a decimal in 0..2^32-1, and wider than 15 bits;
- never repeats across reads;
- RANDOM=42 does not repeat it;
- fresh in a command substitution;
- an assignment is ignored.
- Both work in arithmetic and under set -u.
develop fails it at the first line; this branch matches bash, three runs
in a row under ASan with leak detection.
Refs #134
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RAmeHfJNm7XjYbMNrQqvkG
This was referenced Sep 26, 2026
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.
What & why
Section 6 of #134:
$EPOCHSECONDSwas already there, but it only counts whole seconds, which is too coarse to time a command from a prompt hook. The usual workaround,date +%s%N, forks once per prompt. bash 5.1 has both variables.Change.
expand_special_dyn_time(new file,src/environment/expand_dyn_time.c) is the tier afterexpand_special_dyn. That function had no room left under the norm's 25-line limit, so it now ends by calling the new one.$EPOCHREALTIMEisclock_gettime(CLOCK_REALTIME), formatted the way bash does: the seconds, a., and six digits of microseconds.$SRANDOMis 32 bits fromgetentropy(). bash promises that$SRANDOMcannot be seeded, so it never comes from the$RANDOMgenerator.getentropyis available on glibc 2.25+ (the oldest CI image, Ubuntu 22.04, has 2.35), on musl and on macOS. If the call fails, the fallback is the session PRNG's full 32 bits.Both borrow
state->linebuf, as$SECONDSdoes. Like the other dynamic variables, assigning to them does not freeze them.Refs #134
How I verified it
Reproduced first.
tests/scripts/60_epochrealtime_srandom.shis graded againstbash --posix5.3.9. The values change on every read, so it prints properties, never values:EPOCHREALTIME:sec.usecformat;EPOCHSECONDSread just before and just after;sleep 0.05;SRANDOM:RANDOM=42does not make it repeat;set -u.develop fails it at the first line. This branch matches bash, three runs in a row under ASan with leak detection. A false failure would need two independent 32-bit draws to be equal (a chance of about 2⁻³²), or
sleep 0.05taking more than 5 s.Local gates on this commit (ASan debug build):
tests/tester: 5363/5363;tests/run_scripts.shagainstbash --posix: 125/125;verify_alloc.sh: identical output on both heaps;alloc_stress.sh: all clean;tests/pty_suite.sh: 109 ok, 6 skipped, 4 failed.prompt_compat_matrix,prompt_drift_matrixandprompt_jobs_badge. They expect the non-root%/$prompt and get#, because the container runs as root; they fail the same way on develop, and CI runs them as a normal user.zsh_glob_test'seqsub/reads-back:cat =(echo hi)printed nothing. That is an older race, about one run in 150 on develop too, and unrelated to this change. Its root cause and fix are in their own PR (fix/eqsub-unique-temp).norminetteis OK on every touched file.src/environment/README.mdlists the new file.Notes / trade-offs
RANDOM=Nstill does not reseed$RANDOM, which is a divergence already documented inexpand2.c. The test does not assert it either way; it only checks that$SRANDOMis unaffected.🤖 Generated with Claude Code
https://claude.ai/code/session_01RAmeHfJNm7XjYbMNrQqvkG
Generated by Claude Code