How to create a key binding re-sourcing the shell configuration without a new command being saved in the history?

I would like to create a key binding, using the key sequence C-x r, to reload the configuration of bash, stored in ~/.bashrc, and the one of the readline library stored in ~/.inputrc.

To reload the configuration of readline, I think I could use the re-read-init-file function which is described in man 3 readline:

re-read-init-file (C-x C-r)
        Read in the contents of the inputrc file, and incorporate any bindings or variable  assignments  found there.

To reload the configuration of bash, I could use the source or . command. However, I’m not sure what’s the best way to combine a shell command with a readline function. So, I came up with the combination of 2 key bindings:

bind '"\C-xr":      ". ~/.bashrc \C-x\C-z1\C-m"'
bind '"\C-x\C-z1":  re-read-init-file'

When I hit C-x r in bash, here’s what happens:

. ~/.bashrc    `~/.bashrc` is inserted on the command line
C-x C-z 1      `C-x C-z 1` is typed which is bound to `re-read-init-file`
C-m            `C-m` is hit which executes the current command line

It seems to work because, inside tmux, if I have 2 panes, one to edit ~/.inputrc or ~/.bashrc, the other with a shell, and I change a configuration file, after hitting C-x r in the shell, I can see the change taking effect (be it a new alias or a new key binding), without the need to close the pane to reopen a new shell.

But, is there a better way of achieving the same result? In particular, is it possible to execute the commands without leaving an entry in the history? Because if I hit C-p to recall the last executed command, I get . ~/.bashrc, while I would prefer to directly get the command which was executed before I re-sourced the shell configuration.

I have the same issue with zsh:

bindkey -s '^Xr' '. ~/.zshrc^M'

Again, after hitting C-x r, the command . ~/.zshrc is logged in the history. Is there a better way to re-source the config of zsh?

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

Don’t inject a command into the command line to run it! That’s very brittle — what you’re trying assumes that there’s nothing typed at the current prompt yet. Instead, bind the key to a shell command, rather than binding it to a line edition command.

In bash, use bind -x.

bind -x '"\C-xr": . ~/.bashrc'

If you also want to re-read the readline configuration, there’s no non-kludgy way to mix readline commands and bash commands in a key binding. A kludgy way is to bind the key to a readline macro that contains two key sequences, one bound to the readline command you want to execute and one bound to the bash command.

bind '"\e[99i~": re-read-init-file'
bind -x '"\e[99b~": . ~/.bashrc'
bind '"\C-xr": "\e[99i~\e[99b~"'

In zsh, use zle -N to declare a function as a widget, then bindkey to bind that widget to a key.

reread_zshrc () {
  . ~/.zshrc
}
zle -N reread_zshrc
bindkey '^Xr' reread_zshrc

Solution 2

In man bash, I found the HISTIGNORE environment variable, whose description is the following:

HISTIGNORE

        A  colon-separated list  of patterns  used to  decide which  command
        lines should be saved on the  history list. Each pattern is anchored
        at the  beginning of the line  and must match the  complete line (no
        implicit `*' is  appended). Each pattern is tested  against the line
        after the checks  specified by HISTCONTROL are  applied. In addition
        to the  normal shell  pattern matching  characters, `&'  matches the
        previous history  line. `&'  may be escaped  using a  backslash; the
        backslash  is removed  before  attempting a  match.  The second  and
        subsequent lines  of a multi-line  compound command are  not tested,
        and are added to the history regardless of the value of HISTIGNORE.

So, I exported the following value in ~/.bashrc:

bind '"\C-xr":   ". ~/.bashrc \C-x\C-z1\C-m"'
bind '"\C-x\C-z1":  re-read-init-file'

export HISTIGNORE="clear:history:. ~/.bashrc "

The last line should prevent bash from saving any clear, history and more importantly . ~/.bashrc command.


For zsh, I found the HIST_IGNORE_SPACE option, which is described in man zshoptions:

HIST_IGNORE_SPACE (-g)

        Remove command lines  from the history list when the  first character on
        the line  is a  space, or when  one of the  expanded aliases  contains a
        leading space. Only  normal aliases (not global or  suffix aliases) have
        this behaviour.  Note that the  command lingers in the  internal history
        until the  next command is entered  before it vanishes, allowing  you to
        briefly reuse or edit the line. If you want to make it vanish right away
        without entering another command, type a space and press return.

According to what it says, if this option is enabled, zsh should remove from the history any command beginning with a space. However, it temporarily stays in the internal history, until the next command is executed. So, I set this option in ~/.zshrc, and then redefined the key binding to re-source the configuration of the shell like so:

    setopt HIST_IGNORE_SPACE
    bindkey -s '^Xr' ' . ~/.zshrc^M ^M'
#                     │            │
#                     │            └─ command containing only a space to force `zsh`
#                     │               to “forget“ the previous command immediately
#                     └─ space added to prevent `zsh` from logging the command

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply