1. 在vi中使用变量对文件内容进行替换
源文件file:只包含六个整数
执行变量替换命令:
其中,“:%s/1/\=$JAVA_HOME/g "中需要注意的是"\=$JAVA_HOME"
\= 指示后面为一表达式,而$JAVA_HOME为系统中的环境变量,标识了java主目录。
====================================================================================
我们可以在vim的帮助文档中找到对 \= 的解释。
我们在vi中敲命令":h substitute",打开帮助文档到substitute的位置。
可以看到,替换命令的格式是:
:[range]s[ubstitute]/{pattern}/{string}/[flags] [count]
其中,{string}是要替换成的目标。
我们在这个帮助界面中再输入命令"/\\=",寻找 \= 符号。
可以看到有这样一句话:"when the {string} starts with "\=" it is evaluated as an expression."
====================================================================================
这样,vi使用变量进行替换的功能就了解了。我们再来做一些尝试。
首先,我们将$JAVA_HOME用双引号括起来,并替换掉数字2。
可以看到,用双引号括起来后,双引号里面的字符串不再进行参数展开。(单引号也一样)
我们再进行更加复杂的尝试。
可以看到,s(也即substitute)命令在遇到double-quote时,便将quote中的字符串作为表达式结果,而在遇到"$"号时,则会进行变量的展开。
上图命令中的"."看起来应该是连接字符串的作用,但是还未在文档中找到专门的说明。
如果使用 ${JAVA_HOME}会导致invalid expression的错误发生,也即{}在s命令中无效。
2. 在here document中使用vi替换命令
编写的脚本如下:
脚本1:脚本中替换的命令是基于上文中的内容写的,这个脚本尝试将数字4更换为java主目录。脚本中从vi开始到最后的感叹号属于 "here document"。
执行脚本1,因为没有重定向vi的输出,所以最终的结果会直接显示在终端上,如下所示:
一开始的vim警告很直观,输入确实不是来自终端,而是来自文件。
但是最终显示的结果,数字4没有被替换掉。那是出了什么问题呢?
先来看一下脚本2。
脚本2: 将str赋值为字符串“123”,将数字4替换为123。
执行脚本2,我们看到替换成功了。
脚本1中str取的值是java主目录,也即"/usr/local/jdk6",替换失败了;脚本2中,str取的值是"123",替换便成功了。
立即就可以察觉的是,失败的案例中,str的值包含了"/"符号。我们再去查看vi的帮助文档,可以看到在substitute的帮助文档中,有这样一段话:
"Be careful: The separation character must not appear in the expression!
Consider using a character like "@" or ":". There is no problem if the result
of the expression contains the separation character."
java主目录中的"/"确实和我们使用的separation character是冲突的,但是如红字中所说,分隔符只有出现在表达式本身才会有影响,如果表达式的结果包含分隔符是没有关系的。$str被展开为java主目录后是包含了分隔符,但是$str本身并不包含分隔符。
其实导致失败的原因应该是这里由shell提前执行了一次变量的展开。
也即":%s/4/\=$str/g"在脚本1中先由shell展开为:":%s/4/\=/usr/local/jdk6/g",再交由vi执行替换命令。可以看到,此时,\=后面包含的串中出现了分隔符,所以导致了替换失败。
而":%s/4/\=$str/g"在脚本1中先由shell展开为:":%s/4/\=123/g",再交由vi执行替换命令,vi会将\=后面的123解释为一个字符串,所以替换成功了。
如果尝试将str=123u(或者str="123u"),也即使str包含任意字母,则替换失败,提示123u为invalid expression。
经测试,如果出现在 \= 后的为纯数字串,则vi将其作为基础字符串;若出现在\=后的串中包含字母,则会被当做一个变量。
str=123u和或者str="123u"测试失败,说明在赋值时不论有没有引号,":%s/4/\=$str/g"均被shell展开为":%s/4/\=123u/g",并交给vi。此时,vi会将\=后的123u当做一个变量,并再次尝试展开,便得到了invaild expression的错误。
总之,导致脚本1失败的原因就是shell已经做了一次变量扩展,再将扩展后的命令交给了vi。
这样的话,竟然shell已经对变量扩展了,那么交给vi的命令中也就没有变量了,而是直接的字符串,也即此时"\="符号是多余的了。
在脚本1中,\= 号本来是vi用来标识对$str的解释的,但$str已经被shell展开了,"\="明显多余了,所以不再需要。
在脚本2中,有没有\=号的效果是一样的。因为":%s/4/123/g" 和 ":%s/4/\=123/g"的执行效果相同。
我们在脚本1的基础上,将 "\="号删除。此时,我们仍需要将替换命令的分隔符换掉(不要忘记了上文的"Be careful"),形成如下脚本:
脚本3:尝试将5换成java主目录
执行脚本,我们得到了期望中的结果。
我们再来看一个脚本:
脚本4:尝试将file中的6全部替换为java主目录路径。
之后,我们看到file中的6全被替换为了java主目录路径。
这是因为:%s!6!\="$str"!g被shell展开为:%s!6!\="/usr/local/jdk6"!g并交由vi执行替换了。
上文说过"\="号后,双引号括起的内容被直接当做字符串处理。
总结一下:
1. vi 中 \= 用来标志后面为表达式;
2. 注意 \= 后的表达式本身(而非结果)不能与分隔符相同,一般需考虑采用其他分隔符;
3. 在shell中使用Here Document时,变量会先被shell展开再交由指定命令处理。