Save file as root after editing as non-root - linux

Ok so this happens to me all the time. There has to be a better solution. Let's say you do vim /etc/somefile.conf and then you do i but realize you are not sudo and you can't write. So then I lose my changes by doing :q then sudo !! and make my changes again. Is there a better way to do this?

Try
:w !sudo tee "%"
The w ! takes the entire file and pipes it into a shell command. The shell command is sudo tee which runs tee as superuser. % is replaced with the current file name. Quotes needed for files that have either spaces or any other special characters in their names.

Depending on the extent of your changes, it might be faster to save (:w) your file with a different name, and then use sudo and cat to overwrite the content of the original file:
sudo sh -c 'cat changed > file'
Note that both cp and mv will replace the original file and its attributes (ownership, permissions, ACLs) will be lost. Do not use them unless you know how to fix the permissions afterwards.

Save the file elsewhere (like your home folder) and then sudo mv it to overwrite the original?

Save the changes as another file and the make the approrpiate replacement.

When vim starts up, the statusbar says [readonly], and the first time you try to edit, it says W10: Warning: Changing a readonly file and pauses for a full second. This is enough warning for me to quit and say sudoedit /etc/somefile.conf.
You can enforce this with a plugin: Make buffer modifiable state match file readonly state.

I use zsh templates and function completion.
Specifically this one. If I don't have write permissions, it prompts for my sudo password and automatically runs "sudo vim"…amongst other things.

I used this:
function! SaveAsSudo()
let v=winsaveview()
let a=system("stat -c \%a " . shellescape(expand('%')))
let a=substitute(a,"\n","","g")
let a=substitute(a,"\r","","g")
call system("sudo chmod 666 " . shellescape(expand('%')))
w
call system("sudo chmod " . a . " " . shellescape(expand('%')))
call winrestview(v)
endfunction
I have mapped <F2> to :w<CR>. & <F8> to :call SaveAsSudo()<CR>
Only advantage this answer provides over the sudo tee option is: vim does not complain about unsaved buffer or file modified externally.

Related

How can I save a write protected file in vi?

I'm trying to save my modified "menu.lst" file in vi. When I save the file, vi says: 'menu.lst' is read only.
How can I fix this?
The file you are trying to save is read-only, meaning you cannot modify its contents. It needs to be marked as writable. The process varies depending on your OS. Here are some helpful resources on how to change permissions of files:
For Windows 10: Nibbleguru: How to remove read-only attribute in Windows 10
For Linux (using chmod): TLDP: File Permissions
For macOS: Chron: How to Change File Permission From Read-Only to Read-Write on a Mac
EDIT:
As filbranden pointed out, for Grub's files, you should be opening vi using the sudo command. Grub's files are meant to be modified by root only. You should be opening your files using sudo vi menu.lst instead.
I have this on my vimrc
cnoremap w!! execute 'silent! write !sudo tee % >/dev/null' <bar> edit!
command! SaveAsRoot w !sudo tee %
cnoreabbrev sudow SaveAsRoot
For instant use, just copy to the clipboard and run:
:#+
:SaveAsRoot
The :#+ loads your clipboard into vim memory which allows you to run the given commands while not saved on your vimrc.
People suggest using sudo vi(m) on unix, but this could have unwanted consequences: all commands executed in this window are done by root and so you could accidentally do unwanted things like deleting file or just creating files owned by root.
Instead you should think about using sudoedit instead. It will copy the file to /tmp and open it in $EDITOR (if you are using vim, you should set it in your ~/.profile / ~/.bash_profile).
But beware: Something I stumbled across: the original file is only replaced when you close vim - no matter how often you save! (This is, because you are editing the file in /tmp and not the original).
It is not that I don't use sudo vim but if I do, I am extra cautious about what I do ;) I do it for example if I know that I will need to edit multiple files as root, or that I want to execute other commands from within vim as root (e.g. git)
And something even more important to me: sudo vim is using roots vimrc instead of mine, but with sudoedit I have my own config...

How to stop VIM dereferencing symlinks? [duplicate]

I have the following setup:
mkdir /1
mkdir /1/2
mkdir /1/2/3
ln -s /1/2/3 /1/3
If I do cd /1/3, and then pwd, I get /1/3. If I use pwd -P, I can get /1/2/3, or pwd -L to force /1/3.
In VIM, I'm looking for a way to get the /1/3.
If I open a file in /1/3/foo.txt, and I use something like fnamemodify(bufname(winbufnr(0)), ':p:h'), it returns /1/2/3.
How can I tell it to give me the same directory that pwd would give?
It appears you can't, other than via system('pwd -L'). According to the vim_use mailing list Vim automatically resolves symlinks nowadays.
See the text around :h E773 for rationale; if Vim went by symlinks instead of resolved filename, it'd be possible to have the same file open in two buffers under two different names, and Vim would become confused trying to figure out where the swap file should be. See also in :h version7.txt:
Unix: When editing a file through a symlink the swap file would use the name
of the symlink. Now use the name of the actual file, so that editing the same
file twice is detected.
Short answer:
You may be able to use mount binding as a substitute for symlinks. See man mount.
Long answer:
I had a similar problem, as I have a short symlink to a mounted partition,
/e -> /media/iam/ext4test
I also have a symlink ~/.vimrc -> /e/configs/.vimrc.
I was running into trouble trying to pop into Netrw in the containing directory (I was landing in ~, but I couldn't see a robust way to avoid that, keeping in mind the desire to use Bookmarks, etc).
My solution was, after considering possibly changing the mount point, is that you can add mount points. So after unlink e, I used mount --bind /media/iam/ext4test /e.
Now, if I am in /e/configs/.vimrc and use :edit . (or :e. etc), it will pop me into Netrw in the containing directory.
Edit:
The command mount --bind makes transient changes. For a permanent bind mount, I add the following to /etc/fstab,
# <file system> <mount point> <type> <options> <dump> <pass>
/media/iam/ext4test /e none bind 0 0

How do I make a file writable from inside Vim

I am frequently opening files that are readonly, I would like to know if there is a way to make them writable from inside Vim. I do not mean :w!, I need the file to be writeable after I close Vim.
If you are on a unix based machine you can always just use the unix command.
:!chmod 777 %
http://ss64.com/bash/chmod.html
Otherwise on windows, you should look into the calcs command:
http://www.delawarepchelp.com/system/dos/calcs.htm
The issue comes from the fact that after changing the file attributes Vim will detect it and will try to reload the file and you will loose what you typed.
The best seems to create a function that redefines the autocmd FileChangedShell to do nothing when the attribute change is detected.
See this example of setting file attributes without reloading a buffer where an example is given for making all files executables.
This should do the trick for you
I found this solution elsewhere:
:set readonly!
:set modifiable
my situation was that vim was opening my file, which I chmod 777 from the commandline before opening, as readonly. Not sure if this helps you at all...
edit: forgot the second command. only the combination of the two worked.
you can use :! to execute shell commands. So you could change the user using :!chown myusername filename or change the access right using for example :!chmod o+w filename
Additionally, if the file was writeable only by another user (and you are not owner, so can't chmod it) you might map this command:
:command SuWrite %!sudo tee %
Of course, sudo -u username for other users
!sudo chmod 777 %
When it asks if you want to use the buffer, type "O" for ok.
You can use the SudoWrite command from the vim-eunuch plugin, which enables you to write to a file with sudo even if you forgot to open vim with sudo. It's a great way to eliminate that annoying behavior when you forget.

Indenting in VIM with all the files in Folder

I have a folder containing hundreds of TTL (TeraTermLanguage) files.
Now I wanted indent all these files.
I have created teraterm.vim for indentation and I open a file using VIM and do "gg=G" and whole file gets indented properly.
But is there any way, where I can indent all the files in folder.
I wanted to do with help of Shell. But in VIM I couldnt pass file indent command as the argument to VIM.
Please suggest which is the best way I can do indentation to all the files in VIM.
Much simpler than scripting vim from the bash command line is to use vimscript from inside of vim (or perhaps a much simpler one-liner for scripting vim from the command line). I personally prefer using the arg list for all multi-file manipulation. For example:
:args ~/src/myproject/**/*.ttl | argdo execute "normal gg=G" | update
args sets the arglist, using wildcards (** will match the current directory as well as subdirectories)
| lets us run multiple commands on one line
argdo runs the following commands on each arg (it will swallow up the second |)
execute prevents normal from swallowing up the next pipe.
normal runs the following normal mode commands (what you were working with in the first place)
update is like :w, but only saves when the buffer is modified.
This :args ... | argdo ... | update pattern is very useful for any sort of project wide file manipulation (e.g. search and replace via %s/foo/bar/ge or setting uniform fileformat or fileencoding).
(other people prefer a similar pattern using the buffer list and :bufdo, but with the arg list I don't need to worry about closing current buffers or opening up new vim session.)
Open up a terminal. Type:
$ vim -w indentme.scr foo.c
Then, type this exactly (in command mode):
gg=G:wq
This will close vim, saving the process of indenting all lines in the file to a Vim script called indentme.scr.
Note: indentme.scr will contain a record of all key commands typed, so when you are done indenting the file, don't spend a lot of time using the arrow keys to look around the file, because this will lead to a much larger script and will severely slow down batch operations.
Now, in order to indent all the lines in a file, just type the following command:
$ vim -s indentme.scr unindented-file.c
Vim will flash open-shut (if you're on a fast computer and not editing a huge file), indenting all lines, then saving the file in-place.
Unfortunately, this will only work on one file at a time, but you can scale the functionality easily using sh's for loop:
for filename in *.ttl ; do
vim -s indentme.scr "$filename"
done
Note: This will save-over any file. Unless set bk is in your ~/.vimrc, don't expect a backup to be saved.
I went off of amphetamachine's solution. However, I needed to recursively search through multiple directories. Here's the solution that I used:
$ find . -type f -name '*.ttl' -exec vim -s indentme.scr "{}" \;
Taking reference from the above answers I would like to make this complete.
I will start from the scratch so that a beginner can understand.
Step-1
Install astyle (tool used for formatting ) by using the following command
Open up a terminal. Type:
sudo apt-get install astyle
Step-2 Make sure you have vim installed on your system.Run the below commands from the directory in which your code files are.The below command will create a script that we intend to run recursively so as to beautify each and every file in our directory.(as mentioned in the above answer)
vim -w indentme.scr foo.c
Step-3 Then, type this exactly (in command mode) and press enter
:%!astyle
Step-4Then type this exactly (in command mode) and press enter
:wq
Step-5 Last run this recursively by the following command:
find . -type f -name '*.cpp' -exec vim -s indentme.scr "{}" \;
All your cpp files will be formatted properly.

How can I create a folder if it doesn't exist, from .vimrc?

I don't like how Vim clutters up my folders with backup files, so I have the following line in my .vimrc file:
set backupdir=~/.vim_backup
However, sometimes this folder doesn't exist because of new machines where I am copying my user files over.
How can I create this folder automatically if it doesn't exist, from within .vimrc? Or is there some better way to deal with this situation?
I think this is operating system independent:
if !isdirectory("/my/directory")
call mkdir("/my/directory", "p")
endif
You can put this into your .vimrc:
silent !mkdir ~/.vim_backup > /dev/null 2>&1
This will attempt to create the ~/.vim_backup each time you start vim. If it already exists, mkdir will signal an error, but you'll never see it.
You could use the mkdir() Vim function. It can create intermediate directories and set proper permissions on the directory as well:
if !isdirectory($HOME . "/.vim/backup")
call mkdir($HOME . "/.vim/backup", "p", 0700)
endif
I much prefer the one-liners already posted. However, I thought I'd share what I've been using:
function! EnsureDirExists (dir)
if !isdirectory(a:dir)
if exists("*mkdir")
call mkdir(a:dir,'p')
echo "Created directory: " . a:dir
else
echo "Please create directory: " . a:dir
endif
endif
endfunction
call EnsureDirExists($HOME . '/.vim_backup')
I wanted a solution to this that works on both Linux and on Windows (with Cygwin binaries on the path.) - my Vim config is shared between the two.
One problem is that 'mkdir' is, I think, built-in to the cmd shell on windows, so it can't be overridden by putting the Cygwin mkdir executable earlier in the path.
I solved this by invoking a new bash shell to perform the action: From there, 'mkdir' always means whatever mkdir executable is on the path:
!bash -c "mkdir -p ~/.vim-tmp"
However, this has the problem that. on Windows at least, it pops up a new window for the cmd shell that '!' invokes. Don't want that every time I start vim. Then I discovered the vim function 'system', which invokes a new cmd shell (the icon appears in the Windows taskbar) but the window is minimised. Hence:
call system("bash -c \"mkdir -p ~/.vim-tmp\"")
Ugly but works.
I have a command in my vimrc that enables me to make a directory corresponding to the buffer presently frontmost:
nmap <silent> ,md :!mkdir -p %:p:h<CR>
This can be used to create a directory any place you care to put one locally or remotely via netrw if you the permissions to do so.
This is built into Vim, so you can simply:
call mkdir($HOME . "/tmp")
You can also specify the p flag to have the command operate in the same way mkdir -p would:
call mkdir($HOME . "/tmp", "p")
For details see :help mkdir or the vim manual online.
This is what I am currently using in my .vimrc file and I think it looks quite clean and simple:
if empty(glob($HOME . '/.vim/undodir'))
call mkdir($HOME . '/.vim/undodir', 'p')
endif

Resources