Как работает трюк vim «напиши с sudo»?


1413

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


5
@ Натан: Не :w !sudo cat > %будет работать, а не загрязнять стандартный вывод?
— Бьярке Фрейнд-Хансен

51
@bjarkef - нет, это не работает. В таком случае sudoприменяется к cat, но не к >, поэтому не допускается. Можно попробовать запустить всю команду в подоболочке sudo, например :w !sudo sh -c "cat % > yams.txt", но это тоже не сработает, потому что в подоболочке %- ноль; вы удалите содержимое вашего файла.
— Натан Лонг

3
Я просто хочу добавить, что после ввода этой команды может появиться предупреждающее сообщение. Если это так, нажмите L. Затем вам будет предложено нажать Enter. Сделайте, и, наконец, ваш файл будет сохранен.
— Паблофиумара

9
@NathanLong @knittl: на :w !sudo sh -c "cat >%"самом деле работает так же хорошо, как и sudo tee %потому, что Vim заменяет имя файла %до того, как оно попадет в подоболочку. Однако ни один из них не работает, если в имени файла есть пробелы; Вы должны сделать :w !sudo sh -c "cat >'%'"или :w !sudo tee "%"исправить это.
— Хан Сеул-О,

1
Сохраните с помощью: W и перезагрузите файл: команда W: выполнить ': silent w! Sudo tee%> / dev / null' | :редактировать!
— Диего Роберто Дос Сантос

Ответы:


1613

В :w !sudo tee %...

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

Как указал Евгений У , %действительно означает «текущее имя файла», которое передается, чтобы teeон знал, какой файл перезаписать.

(В командах замещения, он немного отличается, поскольку :help :%показывает, что это equal to 1,$ (the entire file)(спасибо @Orafu для указывая на то , что это не оценивает к имени файла) Например,. :%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 .)

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

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

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


147
Особенно нравится ваша запись "W !!" что так легко запомнить после использования "sudo !!" в командной строке.
— Эйдан Кейн

11
Так что это использует teeдля его способности писать стандартный ввод в файл. Я удивлен, что нет программы, работа которой заключается в том, чтобы сделать это (я нашел программу, о которой я никогда не слышал, spongeкоторая вызывает это). Я предполагаю, что типичное «записать поток в файл» выполняется встроенной оболочкой. Разве Vim !{cmd}не разветвляется ( cmdвместо этого разветвляется )? Возможно, что-то более очевидное - использовать какой-то рабочий вариант, sh -c ">"а не tee.
— Стивен Лу

2
@Steven Lu: spongeявляется частью moreutilsпакета почти во всех дистрибутивах, кроме дистрибутивов на основе Debian. moreutilsимеет довольно хорошие инструменты, которые находятся на одном уровне с более распространенными инструментами, такими как xargsи tee.
— Швейцария,

10
Как расширить этот псевдоним, чтобы он также указывал vim автоматически загружать измененное содержимое файла в текущий буфер? Он спрашивает меня об этом, как автоматизировать это?
— Златко

10
@ user247077: В этом случае catвыполняется от имени пользователя root, а вывод перенаправляется оболочкой, которая не запускается от имени пользователя root. Это так же, как echo hi > /read/only/file.
— WhyNotHugo

99

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


1
Интересно, :help _%поднимает то, что вы ввели, но :help %поднимает ключ соответствия скобок. Я бы не подумал попробовать префикс подчеркивания, это какой-то шаблон в документации vim? Есть ли какие-то «особые» вещи, которые стоит попробовать при поиске помощи?
— Дэвид Папа

2
@David: helpкоманда переходит на тег. Вы можете увидеть доступные теги с :h help-tags. Вы также можете использовать завершение командной строки, чтобы увидеть совпадающие теги: :h cmdline<Ctrl-D>(или, :h cmdline<Tab>если вы установили wildmodeсоответственно)
— Евгений Ярмаш

1
Мне пришлось использовать cmap w!! w !sudo tee % > /dev/nullв моем файле .vimrc, чтобы сделать эту работу. Является %неуместны в ответе выше? (Здесь нет эксперта по vim.)
— dmmfll

@DMfll Да, это так. Команда в ответе приведет к тому, sudo tee > /dev/null /path/to/current/fileчто на самом деле не имеет смысла. (Собирается редактировать это)
— Jazzpi

1
@jazzpi: Ты не прав. Оболочкам на самом деле все равно, где в командной строке вы делаете перенаправление файлов.
— Евгений Ярмаш

19

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

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

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

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

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


11
Хотя это может работать, но также предоставляет sudo доступ к нескольким программам (sh и cat). Другие примеры могут быть более безопасными, если их заменить teeна, /usr/bin/teeчтобы предотвратить атаки с изменением PATH.
— ИДБРИ

18

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

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

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

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


9

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

Добавьте его к своему 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>как написать символ канала для разделения двух команд здесь.

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


молчать! отключил запрос пароля, чтобы вы его не видели
— Энди Рэй,

7

Я хотел бы предложить другой подход к проблеме "Упс, которую я забыл написать 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для небольших файлов)

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


5
Просто знайте, что это намного менее прощающе. Лично большинство моих ошибок - глупые ошибки. Поэтому я предпочитаю получать хедз-ап, когда я делаю что-то, что может быть и глупым, и косвенным. Это, конечно, вопрос предпочтения, но повышение привилегий должно быть добросовестным актом. Кроме того: если вы испытываете это так часто, чтобы сделать ": w !!" достаточно беспокойства, чтобы автоматически sudo (но только если owner = root); Вы можете проверить свой текущий рабочий процесс.
— Пейн

Интересный комментарий, хотя нужно помнить, потому что при открытии файла rootзапрашивается пароль.
— Августин Ридингер

Ах! Там есть разница Это зависит от того, установлен ли пользователь sudo «NOPASSWD» или нет.
— Пейн

Тогда иметь NOPASSWD - это то, что меньше всего прощает ... :)
— Августин Ридингер,

4

ДЛЯ НЕОВИМ

Из-за проблем с интерактивными вызовами ( https://github.com/neovim/neovim/issues/1716 ) я использую это для neovim, основываясь на ответе доктора Беко:

cnoremap w!! execute 'silent! write !SUDO_ASKPASS=`which ssh-askpass` sudo tee % >/dev/null' <bar> edit!

Откроется диалоговое окно с использованием ssh-askpassзапроса пароля sudo.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.