I have a bash theme that displays various parts of the prompt in a lazy way. So it displays a set of information in the beginning and then lazy load information that takes time to load and rewrite the prompt from a background process.
Now since the rendering of the lazy loading is async if you move away from the current working directory to another one while the async part was not rendered we will get misplaced information in the wrong place. Example:
The async rendering function is called by:
# Check the async side of the prompt if available
set +m
_render_async &
Now to prevent this from happening, I added a check to see before rendering the prompt if i have any background process that is waiting to render that async side that is not in the current directory and kill it.
# Check and kill any irreelvant background jobs
# Outdated background jobs are any gaudi::async_render executed on folders
# other than the current working directory $PWD
export PROMPT_COMMAND="gaudi::kill_outdated_asyncRender; $PROMPT_COMMAND"
# Kill all background gaudi::render_async that are running in the wrong context
# Wrong context is any directory (CWD) that is not the current directory
# USAGE:
# gaudi::kill_outdated_asyncRender
gaudi::kill_outdated_asyncRender() {
joblist="$(jobs | grep '_render_async.*wd:' | cut -d "[" -f2 | cut -d "]" -f1 | tr '\n' ' ')"
IFS=' '
for job in $joblist; do kill "%$job"; done
}
This works perfectly now and i cannot see the wrong rendering, but what I have noticed is that now bash completion on functions is messed up and items do not show as they are intended as follows:
As you can see her the completion elements are not in the same line and its just broken. Removing the kill_outdated_asyncRender
fixes this behaviour but I am still not sure to why this happens.
gaudi::kill_outdated_asyncRender
sets IFS
, but does not reset it. That has global implications, as it's used throughout bash to control word-splitting behavior:
The IFS variable is used in shells (Bourne, POSIX, ksh, bash) as the input field separator (or internal field separator). Essentially, it is a string of special characters which are to be treated as delimiters between words/fields when splitting a line of input.
Modifying IFS
is a common pattern, but you'll want to reset it:
gaudi::kill_outdated_asyncRender() {
joblist="$(jobs | grep '_render_async.*wd:' | cut -d "[" -f2 | cut -d "]" -f1 | tr '\n' ' ')"
local oIFS=$IFS
IFS=' '
for job in $joblist; do kill "%$job"; done
IFS=$oIFS
}
As a general guideline, keep your $PROMPT_COMMAND
dead simple. Obviously there's the latency concern, but also it's pretty hard to debug in itself. Along these lines I'd make a couple of targeted recommendations to reduce the chance of side-effects:
When you spawn off the background process, record its PID into a file. Then, use the contents of this file in your kill function instead of the jobs | grep
pipeline.
Rather than calculating these values upon directory change, pre-compute them (in the background) during login and periodically (via cron). You also have a "refresh" command that updates the values synchronously so you can always get the latest value if you question what you're seeing.