京东6.18大促主会场领京享红包更优惠

 找回密码
 立即注册

QQ登录

只需一步,快速开始

Linux环境下VI/VIM编辑文件时无权限保存的办理方法(普通用户)

2024-11-2 22:44| 发布者: 76a9| 查看: 50| 评论: 0

摘要: 在Linux环境下,如果直接使用VI/VIM命令编辑没有修改权限的文件时,保存的时候就会提示用户无法进行保存利用,一般的办理方法只能是关闭文件重新以sudo权限打开该文件编辑后再保存(前提是用户具有sudo权限)。其实,

在Linux环境下,如果直接使用VI/VIM命令编辑没有修改权限的文件时,保存的时候就会提示用户无法进行保存利用,一般的办理方法只能是关闭文件重新以sudo权限打开该文件编辑后再保存(前提是用户具有sudo权限)。其实,在VI/VIM模式下通过一些简朴的命令,就能在不关闭当前文件的环境下到达保存文件的目的。

方法一

关于%! sudo tee % > /dev/null这条命令的说明如下

此命令是把当前文件(即%)作为stdin传给sudo tee命令来执行。

方法二
 

在Linux上工作的朋侪很可能碰到过这样一种环境,当你用Vim编辑完一个文件时,运行:wq保存退出,忽然蹦出一个错误:

E45: 'readonly' option is set (add ! to override)

这表明文件是只读的,按照提示,加上!强制保存::w!,效果又一个错误出现:

"readonly-file-name" E212: Can't open file for writing

文件明显存在,为何提示无法打开?这错误又代表什么呢?查看文档:help E212:

For some reason the file you are writing to cannot be created or overwritten.
The reason could be that you do not have permission to write in the directory
or the file name is not valid.

原来是可能没有权限造成的。此时你才想起,这个文件须要root权限才气编辑,而当前登陆的只是普通用户,在编辑之前你忘了使用sudo来启动Vim,以是才保存失败。于是为了防止修改丢失,你只好先把它保存为另外一个临时文件temp-file-name,然后退出Vim,再运行sudo mv temp-file-name readonly-file-name覆盖原文件。

但这样利用过于繁琐。而且如果只是想暂存此文件,还须要接着修改,则希望保存Vim的工作状态,好比编辑历史,buffer状态等等,该怎么办?能不能在不退出Vim的环境下获得root权限来保存这个文件?

办理方案

答案是可以,执行这样一条命令即可:

[code]:w !sudo tee %[/code]

接下来我们来分析这个命令为什么可以工作。起首查看文档:help :w,向下滚动一点可以看到:

[code] *:w_c* *:write_c* :[range]w[rite] [++opt] !{cmd} Execute {cmd} with [range] lines as standard input (note the space in front of the '!'). {cmd} is executed like with ":!{cmd}", any '!' is replaced with the previous command |:!|. The default [range] for the ":w" command is the whole buffer (1,$)[/code]

把这个使用方法对应前面的命令,如下所示:

[code] : w !sudo tee % | | | | :[range]w[rite] [++opt] !{cmd}[/code]

我们并未指定[code]range[/code],拜见资助文档最下面一行,当[code]range[/code]未指定时,默认环境下是整个文件。此外,这里也没有指定opt。

Vim中执行外部命令

接下来是一个叹号!,它表示其后面部分是外部命令,即[code]sudo tee %[/code]。文档中说的很清晰,这和直接执行[code]:!{cmd}[/code]是一样的效果。后者的作用是打开shell执行一个命令,好比,运行[code]:!ls[/code],会表现当前工作目次下的全部文件,这非常有效,任何可以在shell中执行的命令都可以在不退出Vim的环境下运行,而且可以将效果读入到Vim中来。试想,如果你要在Vim中插入当前工作路径大概当前工作路径下的全部文件名,你可以运行:

[code]:r !pwd或:r !ls[/code]

此时全部的内容便被读入至Vim,而不须要退出Vim,执行命令,然后拷贝粘贴至Vim中。有了它,Vim可以自由的利用shell而无需退出。

命令的另一种表示形式

再看前面的文档:

Execute {cmd} with [range] lines as standard input

以是现实上这个:w并未真的保存当前文件,就像执行:w new-file-name时,它将当前文件的内容保存到另外一个new-file-name的文件中,在这里它相当于一个另存为,而不是保存。它将当前文档的内容写到后面cmd的尺度输入中,再来执行cmd,以是整个命令可以转换为一个具有相同功能的普通shell命令:

[code]$ cat readonly-file-name | sudo tee %[/code]

这样看起来”正常”些了。此中sudo很好理解,意为切换至root执行后面的命令,tee和%是什么呢?

%的意义

我们先来看%,执行[code]:help cmdline-special[/code]可以看到:

[code]In Ex commands, at places where a file name can be used, the following characters have a special meaning. These can also be used in the expression function expand() |expand()|. % Is replaced with the current file name. *:_%* *c_%*[/code]

在执行外部命令时,%会扩展成当前文件名,以是上述的[code]cmd[/code]也就成了[code]sudo tee readonly-file-name[/code]。此时整个命令即:

[code]$ cat readonly-file-name | sudo tee readonly-file-name[/code]

留意:在另外一个地方我们也常常用到%,没错,更换。但是那边%的作用不一样,执行:help :%查看文档:

[code]Line numbers may be specified with: *:range* *E14* *{address}* {number} an absolute line number ... % equal to 1,$ (the entire file) *:%*[/code]

在更换中,%的意义是代表整个文件,而不是文件名。以是对于命令:%s/old/new/g,它表示的是更换整篇文档中的old为new,而不是把文件名中的old换成new。

tee的作用

如今只剩一个难点: tee。它究竟有何用?维基百科上对其有一个详细的表明,你也可以查看[code]man page[/code]。下面这幅图很形象的展示了tee是如何工作的:

[code]ls -l[/code]的输出经过管道传给了tee,后者做了两件事,起首拷贝一份数据到文件file.txt,同时再拷贝一份到其尺度输出。数据再次经过管道传给less的尺度输入,以是它在不影响原有管道的基础上对数据作了一份拷贝并保存到文件中。看上图中间部分,它很像大写的字母T,给数据运动增加了一个分支,tee的名字也由此而来。

如今上面的命令就容易理解了,tee将其尺度输入中的内容写到了[code]readonly-file-name[/code]中,从而到达了更新只读文件的目的。当然这里其实另有另外一半数据:[code]tee[/code]的尺度输出,但因为后面没有跟别的的命令,以是这份输出相当于被扬弃。当然也可以在后面补上> [code]/dev/null[/code],以显式的丢弃尺度输出,但是这对整个利用没有影响,而且会增加输入的字符数,因此只需上述命令即可。

命令执行之后

运行完上述命令后,会出现下面的提示:

[code]W12: Warning: File "readonly-file-name" has changed and the buffer was changed in Vim as well See ":help W12" for more info. [O]K, (L)oad File:[/code]

Vim提示文件更新,询问是确认还是重新加载文件。建议直接输入O,因为这样可以保存Vim的工作状态,好比编辑历史,buffer等,撤消等利用仍旧可以继承。而如果选择L,文件会以全新的文件打开,全部的工作状态便丢失了,此时无法执行撤消,buffer中的内容也被清空。

更简朴的方案:映射

上述方式非常完善的办理了文章开始提出的问题,但毕竟命令还是有些长,为了制止每次输入一长串的命令,可以将它映射为一个简朴的命令加到.vimrc中:

[code]" Allow saving of files as sudo when I forgot to start vim using sudo. cmap w!! w !sudo tee > /dev/null %[/code]

这样,简朴的运行:w!!即可。命令后半部分> /dev/null在前面已经表明过,作用为显式的丢掉尺度输出的内容。

另一种思绪

至此,一个比力完善但很tricky的方案已经完成。你可能会问,为什么不消下面这样更常见的命令呢?这不是更容易理解,更简朴一些么?

[code]:w !sudo cat > %[/code]

重定向的问题

我们来分析一遍,像前面一样,它可以被转换为相同功能的shell命令:

[code]$ cat readonly-file-name | sudo cat > %[/code]

这条命令看起来一点问题没有,可一旦运行,又会出现另外一个错误:

[code]/bin/sh: readonly-file-name: Permission denied shell returned 1[/code]

这是怎么回事?不是明显加了sudo么,为什么还提示说没有权限?稍安勿躁,原因在于重定向,它是由shell执行的,在统统命令开始之前,shell便会执行重定向利用,以是重定向并未受sudo影响,而当前的shell本身也是以普通用户身份启动,也没有权限写此文件,因此便有了上面的错误。

重定向方案

这里先容了几种办理重定向无权限错误的方法,当然除了[code]tee[/code]方案以外,另有一种比力方便的方案:以[code]sudo[/code]打开一个[code]shell[/code],然后在该具有root权限的shell中执行含重定向的命令,如:

[code]:w !sudo sh -c 'cat > %'[/code]

可是这样执行时,由于单引号的存在,以是在Vim中%并不会展开,它被原封不动的传给了shell,而在shell中,一个单独的%相当于nil,以是文件被重定向到了nil,全部内容丢失,保存文件失败。

既然是由于%没有展启发致的错误,那么试着将单引号'换成双引号"再试一次:

[code]:w !sudo sh -c "cat > %"[/code]

成功!这是因为在将命令传到shell去之前,%已经被扩展为当前的文件名。有关单引号和双引号的区别可以参考这里,简朴的说就是单引号会将其内部的内容原封不动的传给命令,但是双引号会展开一些内容,好比变量,转义字符等。

当然,也可以像前面一样将它映射为一个简朴的命令并添加到.vimrc中:

[code]" Allow saving of files as sudo when I forgot to start vim using sudo. cmap w!! w !sudo sh -c "cat > %"[/code]

留意:这里不再须要把输出重定向到[code]/dev/null[/code]中。

写在结尾

至此,借助Vim强大的灵活性,实现了两种方案,可以在以普通用户启动的Vim中保存需root权限的文件。两者的原理雷同,都是使用了Vim可以执行外部命令这一特性,区别在于使用差别的shell命令。如果你另有别的的方案,欢迎给我留言。

(全文完)

feihu

2014.07.30 于 Shenzhen


来源:https://www.jb51.net/LINUXjishu/608817.html
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
关闭

站长推荐上一条 /6 下一条

QQ|手机版|小黑屋|梦想之都-俊月星空 ( 粤ICP备18056059号 )|网站地图

GMT+8, 2025-7-1 18:30 , Processed in 0.047087 second(s), 18 queries .

Powered by Mxzdjyxk! X3.5

© 2001-2025 Discuz! Team.

返回顶部