vi如何退出

vi编辑完文件需要保存文件和退出。一般是esc,然后:wq或者:x来退出.但是两者还是有一些不同的.

:wq 的逻辑是”保存文件并退出”

:x 的逻辑是”检查是否有更新若有更新则保存并退出否则直接退出”

前者最后的效果是你无论是否对文件是否做出改动，文件在系统中的timestamp都会更新,而后者只会在你有改动后才会更新.

这又有什么问题呢?一些程序会询问系统，将文件timestamp是否更新作为判断文件是否有改动的标准。若你每次都用wq,那么它会在你每次vi打开某文件后更新ts.若某次你只是浏览了下一组文件，但在程序看来，你把这些文件全改动过了.在某些时候会给这些程序造成一些困扰.

比如说,make程序就是这么个例子.

首先，我们若有两个文件如下

$ 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 Makefile

文件内容也很简单

#include

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

其实就是将main.c文件编译然后输出二进制文件val。

我们注意看main.c的时间: Dec 31 22:16。

然后执行make,再查看文件如下:

$ 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 val

我们可以看到,all依赖main.o,main.o依赖main.c,但因为all是.PHONY的,所以永远执行all.现在因为没有任何的编译过程,所以make推断首先执行main.o,然后执行all,最后得到结果文件main.o和val

我们可以尝试再来make一下

$ 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 val

我们可以发现all程序又执行了下,但main.o并没有更新,因为main.c的ts没有变动.对比这次和上次,我们可以发现只有val,也就是all最后生成的val文件更新了下ts.

我们vi打开,用:x退出,查看ts并重新编译,效果如下:

$ 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 val

我们可以看到,一切如故,只是更新下val.

那么,若用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 val

我们看到make多执行了一步,重新编译生成了下main.o.虽然我其实并没有任何改动.

在这个例子中,因为文件本身非常小,编译的速度其实非常之快,多编译还是少编译一两个这样的文件其实并没有什么关系.但是在实际工程中并没有这么轻松.实际工程中我们可能要面对大量源码文件在一个工程中,一次完整的make耗费的时间可能可以让你去吃一顿饭了.很多时候,我们可能只是查看了一堆文件，而只是修改了某一两个.c，若用wq,这意味着所有你看过的文件都要重新编译,所有依赖它生成的.o文件的文件也都要重新编译.若你还浏览了某个.h文件,那么可能涉及的文件更广了–而这其实本可以避免.

当然,我们可以用:q!来不保存退出,用:q来保存退出.但个人作为一个懒人还是比较喜欢用一个:x,至少–即便出问题了git stash this_file 就行了.

PS: 这个月一直忙着做其它事情，直到最后一天发现若不写点什么本月就没有内容了，所以随便草稿箱找了个本不打算写的topic完善了下,也是蛮拼的…