Вопрос: Как работает трюк vim «write with sudo»?


Многие из вас, вероятно, видели команду, которая позволяет вам писать на файл, который требует разрешения root, даже если вы забыли открыть vim с помощью sudo:

:w !sudo tee %

Дело в том, что я не понимаю, что здесь происходит.

Я уже понял это: wдля этого

                                                        *: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 |:!|.

поэтому он передает все строки в качестве стандартного ввода.

!sudo teeчастичные звонки teeс правами администратора.

Чтобы все имело смысл, %должен выводить имя файла (в качестве параметра для tee), но я не могу найти ссылки на помощь для этого поведения.

ТЛ; др Может ли кто-нибудь помочь мне проанализировать эту команду?


1121


источник


Ответы:


%означает "текущий файл"

В виде Евгений указывал , %действительно означает «текущее имя файла». Другое использование для этого в Vim - в командах замены. Например, :%s/foo/barозначает " в текущем файле , замените вхождения fooс bar. "Если вы выделите текст перед вводом текста :s, вы увидите, что выделенные линии заменяют %как ваш диапазон замещения.

:wне обновляет файл

Одна запутанная часть этого трюка состоит в том, что вы можете подумать :wизменяет ваш файл, но это не так. Если вы открыли и file1.txt, затем побежал :w file2.txt, это было бы «сохранить как»; file1.txtне будет изменено, но текущее содержимое буфера будет отправлено file2.txt,

Вместо file2.txt, ты можешь замените команду оболочки для получения содержимого буфера , Например, :w !catбудет просто отображать содержимое.

Если Vim не был запущен с доступом sudo, его :wне может изменить защищенный файл, но если он передает содержимое буфера в оболочку, команда в оболочке Можно работать с sudo , В этом случае мы используем tee,

Понимание тройника

Что касается tee, teeкоманды в виде Т-образной трубы в нормальной ситуации трубопровода bash: она направляет вывод в указанный файл (ы) и также отправляет его на стандартный вывод , который может быть захвачен следующей командой.

Например, в ps -ax | tee processes.txt | grep 'foo', список процессов будет записан в текстовый файл а также передал grep,

     +-----------+    tee     +------------+
     |           |  --------  |            |
     | ps -ax    |  --------  | grep 'foo' |
     |           |     ||     |            |
     +-----------+     ||     +------------+
                       ||   
               +---------------+
               |               |
               | processes.txt |
               |               |
               +---------------+

(Диаграмма, созданная с помощью Asciiflow .)

См. teeсправочная страница для получения дополнительной информации.

Тройник в руке

В ситуации, которую ваш вопрос описывает, с помощью teeэто взлом, потому что мы игнорируем половину того, что он делает , sudo teeзаписывает в наш файл, а также отправляет содержимое буфера на стандартный вывод, но мы игнорируем стандартный вывод , В этом случае нам не нужно передавать что-либо другой команде; мы просто используем teeкак альтернативный способ записи файла, и чтобы мы могли его вызвать sudo,

Сделать этот трюк легким

Вы можете добавить это в свой .vimrcсделать этот трюк простым в использовании: просто введите :w!!,

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

> /dev/nullчасть эксплицитно выбрасывает стандартный вывод, так как, как я уже сказал, нам не нужно передавать что-либо другой команде.


1251



В выполненной командной строке, %означает текущее имя файла , Это описано в :help cmdline-special:

In Ex commands, at places where a file name can be used, the following
characters have a special meaning.
        %       Is replaced with the current file name.

Как вы уже выяснили, :w !cmdпереводит содержимое текущего буфера в другую команду. Какие teeэто стандартный ввод текста в один или несколько файлов, а также стандартный вывод. Следовательно, :w !sudo tee % > /dev/nullэффективно записывает содержимое текущего буфера в текущий файл в то время как корень , Другая команда, которая может быть использована для этого, - это dd:

:w !sudo dd of=% > /dev/null

В качестве ярлыка вы можете добавить это сопоставление в свой .vimrc:

" Force saving files that require root permission 
cnoremap w!! w !sudo tee > /dev/null %

С вышесказанным вы можете ввести :w!!<Enter>для сохранения файла с правами root.


86



Это также хорошо работает:

:w !sudo sh -c "cat > %"

Это вдохновляет комментарий @Nathan Long.

УВЕДОМЛЕНИЕ :

"должны использоваться вместо 'потому что мы хотим %перед тем как перейти к оболочке.


17



:w- Записать файл.

!sudo- Вызовите команду оболочки sudo.

tee- Выход команды write (vim: w) перенаправляется с помощью tee. % - не что иное, как текущее имя файла i.e. /etc/apache2/conf.d/mediawiki.conf. Другими словами, команда tee запускается как root, и она принимает стандартный ввод и записывает его в файл, представленный%. Однако это заставит перезагрузить файл снова (нажмите L, чтобы загрузить изменения в самом vim):

ссылка на учебник


16



The accepted answer covers it all, so I'll just give another example of a shortcut that I use, for the record.

Add it to your etc/vim/vimrc (or ~/.vimrc):

  • cnoremap w!! execute 'silent! write !sudo tee % >/dev/null' <bar> edit!

Where:

  • cnoremap: tells vim that the following shortcut is to be associated in the command line.
  • w!!: the shortcut itself.
  • execute '...': a command that execute the following string.
  • silent!: run it silently
  • write !sudo tee % >/dev/null: the OP question, added a redirection of messages to NULL to make a clean command
  • <bar> edit!: this trick is the cherry of the cake: it calls also the edit command to reload the buffer and then avoid messages such as the buffer has changed. <bar> is how to write the pipe symbol to separate two commands here.

Hope it helps. See also for other problems:


1



I'd like to suggest another approach to the "Oups I forgot to write sudo while opening my file" issue:

Instead of receiving a permission denied, and having to type :w!!, I find it more elegant to have a conditional vim command that does sudo vim if file owner is root.

This is as easy to implement (there might even be more elegant implementations, I'm clearly not a bash-guru):

function vim(){
  OWNER=$(stat -c '%U' $1)
  if [[ "$OWNER" == "root" ]]; then
    sudo /usr/bin/vim $*;
  else
    /usr/bin/vim $*;
  fi
}

And it works really well.

This is a more bash-centered approach than a vim-one so not everybody might like it.

Of course:

  • there are use cases where it will fail (when file owner is not root but requires sudo, but the function can be edited anyway)
  • it doesn't make sense when using vim for reading-only a file (as far as I'm concerned, I use tail or cat for small files)

But I find this brings a much better dev user experience, which is something that IMHO tends to be forgotten when using bash. :-)


1