Wspólne zastrzeżenia
Najczęstszą metodą obejścia problemu z plikiem tylko do odczytu jest otwarcie potoku do bieżącego pliku jako superużytkownik za pomocą implementacji sudo tee
. Jednak wszystkie najpopularniejsze rozwiązania, które znalazłem w Internecie, mają kombinację kilku potencjalnych zastrzeżeń:
- Cały plik zostanie zapisany w terminalu, a także plik. Może to być powolne w przypadku dużych plików, szczególnie w przypadku wolnych połączeń sieciowych.
- Plik traci tryby i podobne atrybuty.
- Ścieżki plików z nietypowymi znakami lub spacjami mogą nie być obsługiwane poprawnie.
Rozwiązania
Aby obejść wszystkie te problemy, możesz użyć następującego polecenia:
" 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'
Można je z szacunkiem skrócić:
:sil exec 'w !sudo tee ' . shellescape(@%, 1) . ' >/dev/null'
:sil exec 'w !sudo tee ' . shellescape(@%, 1) . ' >NUL'
Wyjaśnienie
:
rozpoczyna polecenie; musisz wpisać ten znak w trybie normalnym, aby rozpocząć wprowadzanie polecenia. Należy to pominąć w skryptach.
sil[ent]
pomija dane wyjściowe polecenia. W takim przypadku chcemy zatrzymać Press any key to continue
monit podobny do polecenia, który pojawia się po uruchomieniu :!
polecenia.
exec[ute]
wykonuje ciąg jako polecenie. Nie możemy tak po prostu uruchomić, :write
ponieważ nie przetworzy wymaganego wywołania funkcji.
!
reprezentuje :!
polecenie: jedyne polecenie, które :write
akceptuje. Zwykle :write
akceptuje ścieżkę do pliku, do którego należy zapisać. :!
samodzielnie uruchamia polecenie w powłoce (na przykład przy użyciu bash -c
). Za pomocą :write
uruchomi polecenie w powłoce, a następnie zapisze cały plik do stdin
.
sudo
powinno być oczywiste, ponieważ właśnie dlatego tu jesteś. Uruchom polecenie jako superużytkownik. W sieci jest mnóstwo informacji o tym, jak to działa.
tee
potoki stdin
do podanego pliku. :write
napisze do stdin
, a następnie superużytkownik tee
otrzyma zawartość pliku i zapisze plik. Nie utworzy nowego pliku - po prostu nadpisze zawartość - więc tryby plików i atrybuty zostaną zachowane.
shellescape()
zmienia znaki specjalne w podanej ścieżce do pliku odpowiednio do bieżącej powłoki. Za pomocą tylko jednego parametru zwykle zawiera ścieżkę w cudzysłowie, jeśli to konieczne. Ponieważ wysyłamy do wiersza poleceń pełnej powłoki, będziemy chcieli przekazać wartość niezerową jako drugi argument, aby umożliwić odwrotne ukośniki innym znakom specjalnym, które w przeciwnym razie mogłyby wyzwolić powłokę.
@%
odczytuje zawartość %
rejestru, który zawiera nazwę pliku bieżącego bufora. Nie musi to być ścieżka bezwzględna, więc upewnij się, że nie zmieniłeś bieżącego katalogu. W niektórych rozwiązaniach pominięty zostanie symbol reklamowy. W zależności od lokalizacji %
jest prawidłowym wyrażeniem i ma taki sam efekt jak odczyt %
rejestru. Zagnieżdżony w innym wyrażeniu skrót jest jednak ogólnie niedozwolony: tak jak w tym przypadku.
>NUL
i >/dev/null
przekieruj stdout
do zerowego urządzenia platformy. Mimo że uciszyliśmy polecenie, nie chcemy, aby cały narzut związany z przesyłaniem potoków stdin
wrócił do vima - najlepiej zrzucić go jak najwcześniej. NUL
jest zerowym urządzeniem w DOS, MS-DOS i Windows, a nie jest prawidłowym plikiem. Od Windows 8 przekierowania do NUL nie powodują zapisania pliku o nazwie NUL. Spróbuj utworzyć plik na pulpicie o nazwie NUL, z rozszerzeniem lub bez: nie będzie można tego zrobić. (Istnieje kilka innych nazw urządzeń w systemie Windows, które warto poznać).
~ / .vimrc
Zależny od platformy
Oczywiście nadal nie chcesz ich zapamiętywać i wpisywać za każdym razem. Znacznie łatwiej jest zamapować odpowiednie polecenie na prostsze polecenie użytkownika. Aby to zrobić w systemie POSIX, możesz dodać następujący wiersz do swojego ~/.vimrc
pliku, tworząc go, jeśli jeszcze nie istnieje:
command W silent execute 'write !sudo tee ' . shellescape(@%, 1) . ' >/dev/null'
Umożliwi to wpisanie polecenia: W (z uwzględnieniem wielkości liter), aby zapisać bieżący plik z uprawnieniami superużytkownika - znacznie łatwiej.
Niezależny od platformy
Korzystam z niezależnego od platformy ~/.vimrc
pliku, który synchronizuje się między komputerami, dlatego do mojego dołączyłem funkcję obsługi wielu platform. Oto ~/.vimrc
tylko odpowiednie ustawienia:
#!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