As you have found out, running commands in the background with &
will put them in a subshell, and a subshell can't change variables/environment variables of the parent process. ("can't" in this sentence should be read as "workarounds are ugly".)
So you'll need the source
command to be executed in the current shell. I'll list a few options below.
All of them don't answer exactly the question you gave, but they seem to provide solutions for the actual problem you're trying to solve.
.zshrc
:
# load zsh-defer:
source "$HOME/zsh-defer.plugin.zsh"
... misc. commands ...
defer 'source "$HOME/file"' for later execution:
zsh-defer source "$HOME/file"
It's possible to cue any number of commands (source .zshrc.2
, source .zshrc.3
etc.) with repeated calls to zsh-defer
.
Benefits: handles a number of edge cases that zinit
won't. A more sophisticated solution than sched
.
Performance: zsh-defer
itself should load within a few ms at most.
Implementation: executes the commands from zle
(zsh's line editor).
The zsh/sched
zsh module
(as suggested in previous answer by @StéphaneChazelas)
Simplistic and straight-forward.
.zshrc
:
sched +10 "source $HOME/file" &>/dev/null || source "$HOME/file"
The ... &>/dev/null || ...
part: if sched
is not available, or exits with an error, the file is source'd immediately instead, under the assumption that that's better than not at all.
Details on sched
: see zsh's documentation.
Drawbacks:
- if the command takes noticeable time to execute, it will block the shell while executing
- risk of accidental input if a deferred command unexpectedly asks for user input ("
Yearly check: do <evil thing>? [y/N]
") while you're typing a command
Performance: the sched
command takes a few ms to execute at most.
No extra work needed - do whatever you want in .zshrc
; the prompt will be loaded and ready for interaction (with some caveats) while .zshrc
is being processed.
Performance: the prompt should be visible within some 10 ms from when the shell starts processing .zshrc
.
Implementation: various zsh features and tricks. Takes some extra care, like redirecting (and buffering) stdin while executing commands, to avoid accidental input.
Plugin manager zinit
With zinit
you can use zinit ice wait
for deferred execution of any command, e.g. source-ing a file.
zinit
can be used just for that functionality, no need to use it as a plugin manager and it won't interefere with other plugin managers, if any.
The original zdharma/zinit
is no longer maintained, but these forks are:
I don't think a long-running command blocks the shell, but I haven't tested this myself.
Performance: on my system, zinit
itself loads in about 10 ms, or 5 ms without completions.
Implementation: zinit
adds a shell function to zsh/sched
, which when invoked immediately adds itself back to the sched
table and in addition to that executes the commands the user has set up, using zsh hooks and other zsh features.
Also possibly useful - mentioned for completeness.
Is a small general library for running asynchronous tasks in zsh. It will however run them in a separate process, which won't be able to set environment variables in the parent shell. I believe there are simple workarounds for this though, by assigning a callback function that should allow executing results in the parent shell.
Benefits: Quite widely used, and actively maintained for eight years.
Implementation: Uses zsh/zpty
to launch a pseudo-terminal executing the deferred commands.
Recommended reading
romkatv's (author of zsh-defer
and powerlevel10k) general thoughts and advice on staged zsh startup/lazy-loading/deferred execution.
Bonus FAQ on shell loading performance
Q: Can I compile .zshrc
to wordcode to make it execute faster?
A: It's possible in theory, but easily introduces problems with aliases, missed re-compilations and more. Not recommended.
Q: I now understand the possible problems. I still want to try this.
A: No, really, you don't.
.zshrc
file takes a while to be sourced, it would be nice to delay some of it. Let's say that 5% of the time involved in sourcing.zshrc
is for the most essential things that I might use on the first prompt, and the rest can be moved into.zshrc.2
,.zshrc.3
etc. files that can be sourced later..zshrc
can be sourced normally, while.zshrc.2
can be sourced after the first command is entered and before the 2nd prompt appears, etc. – iconoclast May 28 '22 at 21:39.zshrc
load times, I found this script to be very helpful: https://github.com/avih/time.sh (some minor tweaks needed to execute it withzsh
) – Carl Sep 20 '22 at 11:27