1. Introduction

Since the Vi editor has many commands and options, compiling configuration files has become a kind of art.

In this tutorial, we explore local and global Vi configurations. First, we discuss the editor configuration in general. After that, we turn to user-specific files and the initialization process in theory. Next, we cover the global files that Vi uses. Finally, a standard tool helps us verify which exact files and other sources our installation uses when running.

For brevity, we use vi (Vi) when referencing both the Vi and Vim editors. Where they differ, the reader is free to add and remove m (M) if necessary.

We tested the code in this tutorial on Debian 11 (Bullseye) with GNU Bash 5.1.4. It should work in most POSIX-compliant environments.

2. Vi Configuration Files

The Vi editor has local and global settings files. In fact, both types are rc files named vimrc with and without a . dot prefix. Similar to the local and global options within Vi, the main difference between the files is their priority.

If we run vi with its –version switch, the output provides a sense of the files involved in initializing the editor:

$ vi --version
[...]
   system vimrc file: "$VIM/vimrc"
     user vimrc file: "$HOME/.vimrc"
 2nd user vimrc file: "~/.vim/vimrc"
      user exrc file: "$HOME/.exrc"
       defaults file: "$VIMRUNTIME/defaults.vim"
  fall-back for $VIM: "/usr/share/vim"
[...]

Actually, this list also states the evaluation order.

While the $HOME environment variable is common for UNIX in general, $VIM and $VIMRUNTIME are Vi-specific. Both have special rules with regard to their values. Moreover, $VIMRUMTIME depends on $VIM, which Vi commonly handles according to the environment.

In general, vi reads the global files first so that any user-specific local files can override settings from the former. Still, there are options, such as secure, which may disable local files entirely.

3. User-specific Files and Initialization

In general, Vi user files are in $HOME/.vim. Still, there are two main places when a user-specific vimrc can be:

  • $HOME/.vimrc
  • $HOME/.vim/.vimrc

On UNIX, Vi also tries _ underscore instead of . period when .vimrc isn’t found.

In addition, there is a strict order during initialization, which also involves environment variables:

If any step successfully configures Vi, the rest are ignored.

Finally, the -u option of vi allows us to supply a custom vimrc file path to override most other initialization steps:

$ vi -u /custom/vimrc

Moreover, there are three special uppercase values of the path argument:

  • NORC skips other vimrc files
  • NONE skips any plugins and other vimrc files
  • DEFAULTS acts like NONE but evaluates the $VIMRUNTIME/defaults.vim file

So, which global vimrc files are evaluated and when?

4. Global Files

As already stated, by default, any global Vi configuration files get evaluated before local and user-specific ones. There are a number of possible global files, depending on the Linux distribution, as well as the Vi editor version and compilation options.

4.1. Checking /etc/vimrc

While many sources suggest /etc/vimrc is the global vimrc file, this is commonly not the case. In fact, we can test this by placing a specific option within that file:

!echo 'Evaluated.'

Here, we run a shell command with the :! exclamation point command as one of the lines in /etc/vimrc. Now, if /etc/vimrc is indeed evaluated, running vi would produce the encoded string:

$ vi
[...]
:!echo 'Evaluated.'
                   Evaluated.

Press ENTER or type command to continue

If this doesn’t happen, it’s probably safe to say that /etc/vimrc isn’t part of the initialization process.

4.2. /etc/vim/vimrc and /etc/vim/vimrc.local

On the other hand, /etc/vim/vimrc is commonly evaluated as the usual global configuration. In addition, it sources /etc/vim/vimrc.local when available:

if filereadable("/etc/vim/vimrc.local")
    source /etc/vim/vimrc.local
endif

This way, we can avoid modifying /etc/vim/vimrc directly.

4.3. Files in $VIM

Other global files include the contents of the /usr/share/vim directory since that’s the fallback value of the $VIM variable we saw earlier:

  • /usr/share/vim/vimrc
  • /usr/share/vim/vimrc.tiny

Of course, using a custom $VIM value changes these paths.

4.4. Skeleton Directory

While not exactly global, a copy of the /etc/skel/.vimrc file usually goes to the home directory of new users.

Thus, by default, if /etc/skel/.vimrc exists, and no manual changes have been made to the resulting copy. We can assume commands from it would get executed in the Vi session of every user.

Now, how do we know which actual files Vi is going to load without testing each one? In addition to the –version trick we saw earlier, we have a more robust option.

5. Which Configuration Sources and Files Does My Vi Employ?

To check the exact files our Vi setup probes during initialization for a given user, we can use strace:

$ strace -e trace=stat vi -c':quit'
stat("/usr/share/vim/vim82", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/usr/share/vim", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
[...]

In the call to strace, we use an expression after -e to trace only the stat() system call. In the vi command we pass to strace, we use the -c flag to directly exit the editor with :q[uit] upon start.

The output already shows directories that Vi checks initially. Since, with little exception, we know all main configuration files include the name vimrc, we can pipe from stdout (1) and stderr (2) to grep for further filtering:

$ strace -e trace=stat vi -c':quit' 2>&1 | grep 'vimrc'
stat("/usr/share/vim/vimrc", {st_mode=S_IFREG|0644, st_size=2242, ...}) = 0
stat("/etc/vim/vimrc.local", 0x7ffed1a67680) = -1 ENOENT (No such file or directory)
stat("/home/baeldung/.vimrc", 0x7ffed1a68970) = -1 ENOENT (No such file or directory)
stat("/home/baeldung/.vim/vimrc", 0x7ffed1a68970) = -1 ENOENT (No such file or directory)

In fact, we see that /usr/share/vim/vimrc takes precedence over all local other files because, in this case, they don’t exist.

On the other hand, we can turn to ltrace with the same vi command to get the environment variables that it sources:

$ ltrace -e getenv vi -c':quit'
vi->getenv("VIMRUNTIME")                    = nil
vi->getenv("VIM")                           = nil
vi->getenv("HOME")                          = "/home/baeldung"
vi->getenv("VIM_POSIX")                     = nil
vi->getenv("SHELL")                         = "/bin/bash"
vi->getenv("TMPDIR")                        = nil
vi->getenv("TEMP")                          = nil
vi->getenv("TMP")                           = nil
vi->getenv("CDPATH")                        = nil
vi->getenv("")                              = nil
vi->getenv("VIMRUNTIME")                    = "/usr/share/vim/vim82"
vi->getenv("VIM")                           = nil
vi->getenv("VIMRUNTIME")                    = "/usr/share/vim/vim82"
vi->getenv("VIM")                           = "/usr/share/vim"
vi->getenv("VIM")                           = "/usr/share/vim"
vi->getenv("VIMRUNTIME")                    = "/usr/share/vim/vim82"
vi->getenv("VIM")                           = "/usr/share/vim"
vi->getenv("HOME")                          = "/home/baeldung"
vi->getenv("MLTERM")                        = nil
vi->getenv("TERM")                          = "xterm-256color"
libtinfo.so.6->getenv("TERMINFO")           = nil
libtinfo.so.6->getenv("HOME")               = "/home/baeldung"
libtinfo.so.6->getenv("HOME")               = "/home/baeldung"
libtinfo.so.6->getenv("TERMINFO_DIRS")      = nil
libtinfo.so.6->getenv("TERMINFO")           = nil
libtinfo.so.6->getenv("HOME")               = "/home/baeldung"
libtinfo.so.6->getenv("TERMINFO_DIRS")      = nil
libtinfo.so.6->getenv("LINES")              = nil
libtinfo.so.6->getenv("COLUMNS")            = nil
libtinfo.so.6->getenv("NCURSES_NO_PADDING") = nil
vi->getenv("COLORS")                        = nil
vi->getenv("COLORFGBG")                     = nil
vi->getenv("VIM")                           = "/usr/share/vim"
vi->getenv("VIMINIT")                       = nil
vi->getenv("HOME")                          = "/home/baeldung"
vi->getenv("EXINIT")                        = nil
vi->getenv("HOME")                          = "/home/baeldung"
vi->getenv("VIMRUNTIME")                    = "/usr/share/vim/vim82"
vi->getenv("VIMRUNTIME")                    = "/usr/share/vim/vim82"
vi->getenv("XDG_CONFIG_HOME")               = nil
vi->getenv("XDG_CONFIG_HOME")               = nil
vi->getenv("GNUPGHOME")                     = nil
vi->getenv("GNUPGHOME")                     = nil
vi->getenv("HOME")                          = "/home/baeldung"
vi->getenv("VIMRUNTIME")                    = "/usr/share/vim/vim82"
vi->getenv("VIMRUNTIME")                    = "/usr/share/vim/vim82"
vi->getenv("HOME")                          = "/home/baeldung"
+++ exited (status 0) +++

The -e switch is similar to strace, but ltrace allows us to filter by the getenv() system call, which shows the environment variables read by the process.

In case of any configuration changes or additions, we can verify the order of evaluation with the above commands.

6. Summary

In this article, we discussed the initialization process of the Vi editor when it comes to settings and configuration files.

In conclusion, there are many sources that vi uses to set its initial behavior.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.