Advertências comuns
O método mais comum de solucionar o problema do arquivo somente leitura é abrir um canal no arquivo atual como superusuário usando uma implementação de sudo tee
. No entanto, todas as soluções mais populares que eu encontrei na Internet têm uma combinação de várias advertências em potencial:
- O arquivo inteiro é gravado no terminal, assim como o arquivo. Isso pode ser lento para arquivos grandes, especialmente em conexões de rede lentas.
- O arquivo perde seus modos e atributos semelhantes.
- Os caminhos de arquivo com caracteres ou espaços incomuns podem não ser manipulados corretamente.
Soluções
Para contornar todos esses problemas, você pode usar o seguinte comando:
" On POSIX (Linux/Mac/BSD):
:silent execute 'write !sudo tee ' . shellescape(@%, 1) . ' >/dev/null'
" Depending on the implementation, you might need this on Windows:
:silent execute 'write !sudo tee ' . shellescape(@%, 1) . ' >NUL'
Estes podem ser reduzidos, respeitosamente:
:sil exec 'w !sudo tee ' . shellescape(@%, 1) . ' >/dev/null'
:sil exec 'w !sudo tee ' . shellescape(@%, 1) . ' >NUL'
Explicação
:
inicia o comando; você precisará digitar esse caractere no modo normal para começar a digitar um comando. Deve ser omitido nos scripts.
sil[ent]
suprime a saída do comando. Nesse caso, queremos parar o Press any key to continue
prompt -like que aparece após a execução do :!
comando.
exec[ute]
executa uma string como um comando. Não podemos simplesmente executar, :write
porque não processará a chamada de função necessária.
!
representa o :!
comando: o único comando que :write
aceita. Normalmente, :write
aceita um caminho de arquivo no qual gravar. :!
por si só executa um comando em um shell (por exemplo, usando bash -c
). Com :write
, ele executará o comando no shell e gravará o arquivo inteiro em stdin
.
sudo
deve ser óbvio, já que é por isso que você está aqui. Execute o comando como superusuário. Há muita informação na rede sobre como isso funciona.
tee
canaliza stdin
para o arquivo especificado. :write
gravará em stdin
, o superusuário tee
receberá o conteúdo do arquivo e o gravará. Ele não criará um novo arquivo - apenas substitua o conteúdo - para que os modos e atributos do arquivo sejam preservados.
shellescape()
escapa caracteres especiais no caminho do arquivo, conforme apropriado para o shell atual. Com apenas um parâmetro, normalmente apenas coloque o caminho entre aspas, conforme necessário. Como estamos enviando para uma linha de comando do shell completa, queremos passar um valor diferente de zero como o segundo argumento para permitir a fuga de barra invertida de outros caracteres especiais que, de outra forma, poderiam desarmar o shell.
@%
lê o conteúdo do %
registro, que contém o nome do arquivo do buffer atual. Não é necessariamente um caminho absoluto, portanto, verifique se você não alterou o diretório atual. Em algumas soluções, você verá o símbolo comercial em omitido. Dependendo da localização, %
é uma expressão válida e tem o mesmo efeito que a leitura do%
registro. Aninhado dentro de outra expressão, o atalho geralmente não é permitido: como neste caso.
>NUL
e >/dev/null
redirecione stdout
para o dispositivo nulo da plataforma. Mesmo que tenhamos silenciado o comando, não queremos que toda a sobrecarga associada ao canal de stdin
volta ao vim - o melhor seja despejá-lo o mais cedo possível. NUL
é o dispositivo nulo no DOS, MS-DOS e Windows, não um arquivo válido. No Windows 8, os redirecionamentos para NUL não resultam na gravação de um arquivo chamado NUL. Tente criar um arquivo na área de trabalho chamado NUL, com ou sem uma extensão de arquivo: você não poderá fazer isso. (Existem vários outros nomes de dispositivos no Windows que valem a pena conhecer.)
~ / .vimrc
Dependente da plataforma
Obviamente, você ainda não deseja memorizá-las e digitá-las sempre. É muito mais fácil mapear o comando apropriado para um comando de usuário mais simples. Para fazer isso no POSIX, você pode adicionar a seguinte linha ao seu ~/.vimrc
arquivo, criando-a se ela ainda não existir:
command W silent execute 'write !sudo tee ' . shellescape(@%, 1) . ' >/dev/null'
Isso permitirá que você digite o comando: W (diferencia maiúsculas de minúsculas) para gravar o arquivo atual com permissões de superusuário - muito mais fácil.
Plataforma independente
Eu uso um ~/.vimrc
arquivo independente de plataforma que sincroniza entre computadores, então adicionei a funcionalidade de várias plataformas à minha. Aqui está um ~/.vimrc
com apenas as configurações relevantes:
#!vim
" Use za (not a command; the keys) in normal mode to toggle a fold.
" META_COMMENT Modeline Definition: {{{1
" vim: ts=4 sw=4 sr sts=4 fdm=marker ff=unix fenc=utf-8
" ts: Actual tab character stops.
" sw: Indentation commands shift by this much.
" sr: Round existing indentation when using shift commands.
" sts: Virtual tab stops while using tab key.
" fdm: Folds are manually defined in file syntax.
" ff: Line endings should always be <NL> (line feed #09).
" fenc: Should always be UTF-8; #! must be first bytes, so no BOM.
" General Commands: User Ex commands. {{{1
command W call WriteAsSuperUser(@%) " Write file as super-user.
" Helper Functions: Used by user Ex commands. {{{1
function GetNullDevice() " Gets the path to the null device. {{{2
if filewritable('/dev/null')
return '/dev/null'
else
return 'NUL'
endif
endfunction
function WriteAsSuperUser(file) " Write buffer to a:file as the super user (on POSIX, root). {{{2
exec '%write !sudo tee ' . shellescape(a:file, 1) . ' >' . GetNullDevice()
endfunction
" }}}1
" EOF