Как работает трюк vim "напиши с sudo"? - PullRequest
1283 голосов
/ 08 апреля 2010

Многие из вас, вероятно, видели команду, позволяющую писать в файл, для которого требуется разрешение 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), но я не могу найти ссылки на справку для этого поведения.

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

Ответы [ 6 ]

1452 голосов
/ 16 августа 2011

В :w !sudo tee % ...

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

Как указал eugene y , % действительно означает «текущее имя файла». Другое использование для этого в 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 part явно отбрасывает стандартный вывод, поскольку, как я уже сказал, нам не нужно ничего передавать другой переданной команде.

93 голосов
/ 08 апреля 2010

В исполняемой командной строке % обозначает имя текущего файла . Это задокументировано в :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.

18 голосов
/ 29 июля 2014

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

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

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

ВНИМАНИЕ

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

16 голосов
/ 04 июня 2011

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

!sudo - вызов команды shell sudo.

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

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

6 голосов
/ 13 января 2018

Принятый ответ охватывает все, так что я просто приведу еще один пример ярлыка , который я использую для записи.

Добавьте его к вашему etc/vim/vimrc (или ~/.vimrc):

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

Где:

  • cnoremap: сообщает vim , что следующий ярлык должен быть связан в командной строке.
  • w!!: сам ярлык.
  • execute '...': команда, которая выполняет следующую строку.
  • silent!: запустить его тихо
  • write !sudo tee % >/dev/null: вопрос ОП, добавлено перенаправление сообщений на NULL, чтобы сделать чистую команду
  • <bar> edit!: этот трюк - самое главное: он также вызывает команду edit, чтобы перезагрузить буфер и избежать сообщений, таких как , буфер изменился . <bar> - это как написать символ pipe , чтобы разделить две команды здесь.

Надеюсь, это поможет. Смотрите также о других проблемах:

4 голосов
/ 28 февраля 2018

Я хотел бы предложить другой подход к "Ой, которые я забыл написать sudo при открытии моего файла" проблема:

Вместо получения permission denied и необходимости ввода :w!! я считаю более элегантным иметь условную команду vim, которая выполняет sudo vim, если владельцем файла является root.

Это так же просто реализовать (могут быть и более элегантные реализации, я явно не баш-гуру):

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

И это действительно хорошо работает.

Это более bash -центрированный подход, чем vim - один, поэтому не всем он может понравиться.

Конечно:

  • есть случаи использования, когда он потерпит неудачу (когда владелец файла не root, но требует sudo, но функция может быть отредактирована в любом случае)
  • не имеет смысла при использовании vim только для чтения файла (насколько я понимаю, я использую tail или cat для небольших файлов)

Но я нахожу, что это дает гораздо лучший dev пользовательский опыт , о котором ИМХО, как правило, забывают при использовании bash. : -)

...