7

I recently read an answer here that included the following advice:

However, as a rule, ⚠️you should not source ~/.zshrc. Depending on what's in your dotfiles, this can lead to all sorts of problems.

I asked the OP for an example of why this could be a problem and was told:

Because it's been sourced already when your shell started up and the order in which commands are run can matter. As a simple example, put ls() { command ls -x "$@" }; alias ls='ls -AF' into your .zshrc file, restart your shell, then source ~/.zshrc. Now you get an error zsh: defining function based on alias ls'. And that's only a fairly innocuous example. Things can get much more hairy than that

That does sound plausible, although I couldn't reproduce that error with zsh. Despite its plausibility, I am having trouble imagining a situation where this could cause any serious issues and those issues would not also occur when opening a clean new session. Worst case scenario I can think of is getting an error message, and I don't even know how to make that happen either.

So, is there good reason to avoid manually sourcing a shell's rc file after making changes to it so that the changes are imported into the current shell session?

I know I have been doing this with bash for many years: I will regularly add a function, or make a change to my ~/.bashrc and then . ~/.bashrc to source it. Are there really cases where that could cause any serious problem? I suppose we can find some edge cases, but are those enough to justify such an empathetic warning? Perhaps there's something specific to zsh that I haven't encountered as a bash user?

I am interested in answers covering any bourne-family shell (bash, sh, zsh, ksh etc.).

15
  • I can imagine a situation where an alias defined later on can affect something earlier on due to the re-sourcing (e.g., you have something that uses grep somewhere early on in .bashrc, and then later you define a custom grep function, and the change in behaviour might break that something when you source .bashrc again in unexpected ways - but you wouldn't see anything wrong in a clean new session).
    – muru
    Commented May 14, 2021 at 10:10
  • @muru why would it be any different in a new session though? You'd still have the same issue wouldn't you?
    – terdon
    Commented May 14, 2021 at 10:12
  • 1
    How could it? In my hypothetical, the breaking grep function can't affect any use of grep in .bashrc before its defined. For a concrete example, say you have something like foo=$(grep -m1 A /etc/os-release) grep () { command grep -H "$@"; } PS1="$foo %u $". The value of $foo will change after you source it again.
    – muru
    Commented May 14, 2021 at 10:20
  • 2
    Sourcing the shell's startup file to incorporate a change in the current shell's environment is first and foremost awfully inelegant. The startup file(s) are for initializing an interactive shell session from a non-initialized one. If you wrote your own .zshrc file, incorporating any changes to it in the current environment should be as easy as using the interactive shell to modify the environment in the appropriate way, mirroring the change in the startup file. Also, suggesting to others to source the file is irresponsible. You don't know what manner of things their script is doing.
    – Kusalananda
    Commented May 14, 2021 at 10:33
  • 2
    If some init file turns dangerous when re-executed in the same shell, I'd say the irresponsible party is the one who created such an init file.
    – ilkkachu
    Commented May 14, 2021 at 12:37

3 Answers 3

2

Regarding the example you have about the init files defining a function and an alias of the same name, assume init.sh with these function and alias definitions (plus calling the alias+function for demonstration):

foo() { echo "foo: $1"; }
alias foo='foo bar'
foo

Bash 4.4 and 5.0:

First time loading the definitions works fine. If this was .bashrc, this would happen when the shell starts.

$ . init.sh
foo: bar

Trying to do it again fails:

$ . init.sh
bash: init.sh: line 1: syntax error near unexpected token `('
bash: init.sh: line 1: `foo() { echo "foo: $1"; }'

Quoting or escaping the function name also doesn't work:

$ \foo() { echo "foo: $1"; }
bash: `\foo': not a valid identifier

You'd need to unalias foo first in init.sh. Or use function foo { ... }, which ignores the alias since foo is not the first word on the command line.

Bash actually expands the alias there, so if the alias just changes one word to another, it'll change the name of the function that gets defined:

$ alias foo=foobar; unset -f foo foobar
$ foo() { echo hi; }
$ typeset -p -f foo
bash: typeset: foo: not found
$ typeset -p -f foobar
foobar () 
{ 
    echo hi
}

Current versions of Zsh give an error, regardless of the contents of the alias:

% . ./init.sh
foo: bar
% . ./init.sh
./init.sh:1: defining function based on alias `foo'
./init.sh:1: parse error near `()'

Quoting or escaping the function name works in zsh, though.

It wasn't always like that, e.g. in zsh 5.3.1 the alias would be expanded when (re)defining the function, and if the alias expands to more than one word, you silently get extra copies of the function. (And if it expands to just one word, that's the name the function gets.) E.g. with first example alias above, both foo and bar would be defined:

% alias foo='foo bar'
% foo() { echo hi; }
% typeset -p -f foo
foo () {
        echo hi
}
% typeset -p -f bar
bar () {
        echo hi
}

Of course none of this would happen if one didn't have both a function and an alias with same name. Instead of that, one could just include all the desired functionality in the function (especially since functions are better than aliases in a few ways anyway).


I am having trouble imagining a situation where this could cause any serious issues

So do I.

You could get an issue if an early part of the init script did something possibly dangerous using command A (which need not be dangerous in itself, it could be used e.g. in a condition), and then a later part of the same script redefined A as a function or an alias in a way that broke the first use.

However, this would be fragile in other ways too. For example, reorganizing the file to move function definitions to the top would also break it, producing the same dangerous behaviour. It would be far better to not have the script rely on the order of function/alias definitions, esp. when running potentially dangerous commands.

3
  • @StéphaneChazelas, ah yep, thanks.
    – ilkkachu
    Commented May 14, 2021 at 15:32
  • Note that there's no such thing as an invalid function name in zsh, like there's no such thing as an invalid executable file name (well except it's impossible to create an executable file with the empty name or name containing NULs while it's possible for zsh functions). Commented May 14, 2021 at 15:50
  • @StéphaneChazelas, which makes perfect sense, actually. I wasn't sure and couldn't verify right now, thanks for the correction.
    – ilkkachu
    Commented May 14, 2021 at 15:59
1

Say you have something like this in your .bashrc:

foo=$(grep -m1 A /etc/os-release)
PS1="$foo %u $"
grep () { command grep -H "$@"; }

A quick way to have the name of the distro in your prompt, and to have grep print always filenames, nothing more, nothing less. If I re-source this, I'll also get the name of the file in my prompt. One might say that this is not unexpected. Now imagine that these lines have a few dozen other bits of shell configuration between them, and may even be in different files which are in turn sourced by your main rc file. (There was an example recently of some software creating an alias for the env command, which could have broken any number of uses.) Then it becomes hard to predict what might go wrong.

I'd say re-sourcing bashrc is tricky even if one wrote it by oneself. A sufficiently long bashrc will likely have tricky interactions with itself.

0

If you're not familiar with what ANY shell's initialization does for/to your system, Don't Use It! Really don't. Don't commit UTBLT! (Using Tool Before Learning Tool) (Using Thing Before Learning Thing).

Most shells provide a -x option that will show the execution of each command. Read the man page for your $SHELL.

"Well written", IMHO, shell initialization scripts inspect environment variables, flag files, etc to determine if they've already run, and "do the right thing".

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .