it-swarm.com.ru

Как работает трюк vim "напиши с судо"?

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

1281
Doppelganger

В :w !Sudo tee %...

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

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

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

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

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

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

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

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

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

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

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

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

Ти как взломать

В ситуации, описанной вашим вопросом, использование 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 явно отбрасывает стандартный вывод, поскольку, как я уже сказал, нам не нужно ничего передавать другой переданной команде.

1451
Nathan Long

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

93
Eugene Yarmash

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

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

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

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

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

18
feihu

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

!Sudo - Вызвать команду Shell Sudo.

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

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

16
kev

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

Добавьте его в свой 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: вопрос OP, добавлено перенаправление сообщений на NULL для создания чистой команды
  • <bar> edit!: этот трюк - самое главное: он также вызывает команду edit, чтобы перезагрузить буфер и избежать сообщений, таких как , когда буфер был изменен . <bar> - это как написать символ pipe для разделения двух команд здесь.

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

5
Dr Beco

Я хотел бы предложить другой подход к проблеме "Oups, которую я забыл написать 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, это то, что IMHO имеет тенденцию забываться при использовании bash. :-)

3
Augustin Riedinger