viAfter editing a file, you need to save it and quit. Typically, you press Esc, then type :wq or :x to exit. However, there are some differences between the two.
- :wq means "save the file and quit"
- :x means "check if there are any changes; save and quit if there are, otherwise quit directly"
The practical effect of the former is that regardless of whether you actually modified the file, the file's timestamp in the system will be updated. The latter only updates the timestamp when you have actually made changes.
So what's the problem? Some programs ask the system whether a file has been modified by checking whether its timestamp has been updated. If you use wq every time, the timestamp will be updated every time you open a file in vi. If one time you just browsed through a bunch of files, from the program's perspective, you modified all of them. This can cause some trouble for these programs in certain situations.
For example, make is one such program.
First, let's say we have two files as follows:
$ ll
total 8
-rw-rw-r--. 1 yu yu 98 Dec 31 22:16 main.c
-rw-rw-r--. 1 yu yu 117 Dec 31 22:15 MakefileThe file contents are also simple:
#include <stdio.h>
int main(int argc, char * argv[])
{
printf("Hello, world!\n");
return 0;
}.PHONY: all clean
all:main.o
gcc -o val main.o
main.o:main.c
gcc -c -o main.o main.c
clean:
rm -rf val main.oBasically, it compiles main.c and outputs the binary file val.
Let's pay attention to the time of main.c: Dec 31 22:16.
Then we run make and check the files as follows:
$ make
gcc -c -o main.o main.c
gcc -o val main.o
$ ll
total 24
-rw-rw-r--. 1 yu yu 98 Dec 31 22:16 main.c
-rw-rw-r--. 1 yu yu 1512 Dec 31 22:19 main.o
-rw-rw-r--. 1 yu yu 117 Dec 31 22:15 Makefile
-rwxrwxr-x. 1 yu yu 8510 Dec 31 22:19 valall depends on main.o, and main.o depends on main.c. But since all is .PHONY, it always executes all. Since there is no compilation process yet, make infers that it should first execute main.o, then all, and finally produces the result files main.o and val.
Let's try running make again:
$ make && ll
gcc -o val main.o
total 24
-rw-rw-r--. 1 yu yu 98 Dec 31 22:16 main.c
-rw-rw-r--. 1 yu yu 1512 Dec 31 22:19 main.o
-rw-rw-r--. 1 yu yu 117 Dec 31 22:15 Makefile
-rwxrwxr-x. 1 yu yu 8510 Dec 31 22:23 valall program ran again, but main.o was not updated because the timestamp of main.c did not change. Comparing this run with the previous one, we can see that only val, the final output of all, had its timestamp updated.
We open it with vi, exit with :x, check the timestamps, and recompile. The effect is as follows:
$ vi main.c && ll && make && ll
total 24
-rw-rw-r--. 1 yu yu 98 Dec 31 22:16 main.c
-rw-rw-r--. 1 yu yu 1512 Dec 31 22:19 main.o
-rw-rw-r--. 1 yu yu 117 Dec 31 22:15 Makefile
-rwxrwxr-x. 1 yu yu 8510 Dec 31 22:23 val
gcc -o val main.o
total 24
-rw-rw-r--. 1 yu yu 98 Dec 31 22:16 main.c
-rw-rw-r--. 1 yu yu 1512 Dec 31 22:19 main.o
-rw-rw-r--. 1 yu yu 117 Dec 31 22:15 Makefile
-rwxrwxr-x. 1 yu yu 8510 Dec 31 22:26 valval's timestamp was updated.
So, what if we exit with wq?
$ vi main.c && ll && make && ll
total 24
-rw-rw-r--. 1 yu yu 98 Dec 31 22:27 main.c
-rw-rw-r--. 1 yu yu 1512 Dec 31 22:19 main.o
-rw-rw-r--. 1 yu yu 117 Dec 31 22:15 Makefile
-rwxrwxr-x. 1 yu yu 8510 Dec 31 22:26 val
gcc -c -o main.o main.c
gcc -o val main.o
total 24
-rw-rw-r--. 1 yu yu 98 Dec 31 22:27 main.c
-rw-rw-r--. 1 yu yu 1512 Dec 31 22:27 main.o
-rw-rw-r--. 1 yu yu 117 Dec 31 22:15 Makefile
-rwxrwxr-x. 1 yu yu 8510 Dec 31 22:27 valWe see that make executed one extra step and recompiled main.o. Even though I didn't actually make any changes.
In this example, because the files themselves are very small and the compilation is extremely fast, recompiling one or two extra files doesn't really matter. But in real-world engineering projects, it's not that easy. In actual projects, we may face a large number of source files in a single project, and a full make might take long enough for you to go grab a meal. Often, we might just look through a bunch of files but only modify one or two .c files. If we use wq, it means all the files we viewed will be recompiled, and so will all files that depend on the .o files generated from them. If you also browsed a certain .h file, the scope of affected files could be even wider — and this could have been avoided.
Of course, we can use :q! to quit without saving, and :q to quit after saving. But as a lazy person, I prefer using :x. At least — if something goes wrong, I can just git stash this_file.
PS: I've been busy with other things this whole month. It wasn't until the last day that I realized if I didn't write something, there would be no content for this month. So I picked a topic from my drafts that I hadn't planned to write, polished it up, and posted it. Pretty desperate...
Comments · 9