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.).

terdon
  • 234,489
  • 66
  • 447
  • 667
  • 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 May 14 '21 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 May 14 '21 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 May 14 '21 at 10:20
  • If *you* wrote your `~/.zshrc` file, then you often know it and could `source` it more than once – Basile Starynkevitch May 14 '21 at 10:30
  • 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 May 14 '21 at 10:33
  • @muru sounds like that should be an answer. I still don't see the problem (I mean, of course `$foo` will change, it has been set up so it does change so I don't find that surprising) so an answer would be better. – terdon May 14 '21 at 11:00
  • Re: (rollback of my edit), just curious: why do you not want to link to the answer? It does appear in the "linked" section ATM via the comment you added to that answer, but that link may go away if that comment is ever deleted. – Stéphane Chazelas May 14 '21 at 11:28
  • @StéphaneChazelas because I didn't want to make it a discussion of that answer and I certainly don't want to make the OP of the answer feel put on the spot. I don't think the specific answer is relevant anyway since this is asking about the general case so just felt it was cleaner to not link to it at all. Also, and perhaps most relevantly, I hadn't realized it was already linked because of my comment... – terdon May 14 '21 at 11:33
  • Fair enough though the context of that answer is relevant as the OP of that Q&A was making an alias that sources ~/.zshrc after changing directory. – Stéphane Chazelas May 14 '21 at 11:41
  • @StéphaneChazelas true, but the warning was a general one (and the OP doesn't seem to be alone, Kusalananda and muru seem to share that position) so I wanted to understand the general case and avoid getting bogged down in the specifics of that example (I can't imagine a situation where sourcing `~/.zshrc` after every `cd` makes sense). – terdon May 14 '21 at 11:46
  • Does this answer your question? [How do I apply the changes to the .zshrc file after editing it?](https://unix.stackexchange.com/questions/532548/how-do-i-apply-the-changes-to-the-zshrc-file-after-editing-it) – Marlon Richert May 14 '21 at 12:22
  • In particular, this answer by Gilles lays out exactly why `source ~/.zshrc` can be problematic (under "Reread `.zshrc`"): https://unix.stackexchange.com/a/532591/413610 – Marlon Richert May 14 '21 at 12:24
  • Actually, the other answers on that question are relevant, too. – Marlon Richert May 14 '21 at 12:30
  • @Kusalananda, well, you're right in that we can't know what people have in their shell's init files, but it seems rather unlikely they'd have something actively dangerous there, given that the init files run each time they launch a new shell or login in any case... Suggesting that someone should logoff and login again is also somewhat common, and necessary to get e.g. group memberships and PAM-assigned settings in effect. That would also involve rerunning the shell's init files. – ilkkachu May 14 '21 at 12:37
  • 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 May 14 '21 at 12:37

3 Answers3

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.

ilkkachu
  • 133,243
  • 15
  • 236
  • 397
  • @StéphaneChazelas, ah yep, thanks. – ilkkachu May 14 '21 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). – Stéphane Chazelas May 14 '21 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 May 14 '21 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.

muru
  • 69,900
  • 13
  • 192
  • 292
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".

waltinator
  • 4,439
  • 1
  • 16
  • 21