I have learned from this Stack Overflow question that it is possible to use vi/vim to comment out a specified range of line numbers. For example, suppose I have the following bash script:
#!/bin/bash This is my very very great script
Now suppose that I want to comment out line numbers 6 through 8 (which contain the words very, very, and great) using the # comment character. In vi/vim, I can simply type :6,8s/^/# to obtain the following:
#!/bin/bash This is my #very #very #great script
which comments out lines 6 through 8.
My question is, is it possible to type a similar one liner that will remove the # comment character from lines 6 through 8 (but not any other commented lines in the file)?
Having said this, I realize that there is some debate about whether I am actually using vi or vim. In practice, I open a file script.sh with the command vi script.sh. Also, when I type the command which vi, I obtain /usr/bin/vi. Nevertheless, when I simply type vi and press Enter, I obtain this:
~ VIM - Vi IMproved ~ ~ version 7.2.330 ~ by Bram Moolenaar et al. ~ Vim is open source and freely distributable ~ ~ Sponsor Vim development! ~ type :help sponsor<Enter> for information ~ ~ type :q<Enter> to exit ~ type :help<Enter> or <F1> for on-line help ~ type :help version7<Enter> for version info
which seems to suggest that I’m actually using vim. I am accessing a remote Ubuntu Linux cluster using SSH from my PC. I am not using a Ubuntu Linux GUI.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
You can use:
:6,8s/^#//
But much easier is to use Block Visual selection mode: Go to beginning of line 6, press Ctrl-v, go down to line 8 and press x.
There is also “The NERD Commenter” plugin.
Method 2
I know your question specifies using vi or vim but here are a few other options for doing this without having to manually open the file:
-
Perl
perl -ne 'if($. <=8 && $. >= 6){s/^s*#//;}print' foo.sh -
Perl version >=5.10
perl -ne '$. ~~ [6..8] && s/^s*#//;print' foo.sh
This will print out the contents of the file, you can either redirect to another (
> new_file.sh) or useito edit the file in place:perl -i -ne '$. ~~ [6..8] && s/^s*#//;print' foo.sh
-
sedsed '6,8 s/^ *#//' foo.sh
Again, to make this edit the original file in place, use
i:sed -i '6,8 s/^ *#//' foo.sh
-
awk/gawketc:gawk '(NR<=8 && NR>= 6){sub("^ *#","")}{print}' foo.sh -
Pure
bash:c=1; while read line; do if [ $c -ge 6 ] && [ $c -le 8 ]; then echo "${line/#/}" else echo $line fi let c++; done < foo.sh
Method 3
vi is a symbolic link to vim in most GNU/Linux distribution so you’re indeed using vim when you type vi.
To remove the comments, you can type: :6,8s/^#// or :6,8s/^s*#// to discard some leading space before the # symbol.
Method 4
AFAIK, vi is usually a symlink of vim nowadays (try which vi or type vi and then follow the symlinks). Maybe, even /usr/bin/vi -> /etc/alternatives/vi -> /usr/bin/vim.basic.
Personally, to remove multiple comment lines, I prefer selecting a vertical block via CtrlV and deleting it. If you need to add a comment symbol to multiple lines, you could CtrlV, then ShiftI, type # and Esc, and a comment will be added to multiple lines.
Method 5
You are probably using vim.tiny. In any case, you can remove the initial comments with:
:6,8s/^#//
Of course, if you insert them in a different way (for instance, with an extra space), you may need to remove whatever else is there. With full vim, visually selecting columns and inserting/deleting characters is an easier way to do the same thing.
Method 6
Personally my Favourite way is using visual block mode
ctrl + v to enter visual block mode, the use the arrow keys or hjkl to select the lines and press x or del.
Want them back ?
ctrl + v make the selection, then I (capital i) #Esc
Method 7
The above answers, using
:6,8s/^#//
are perfect solution, but a bit cumbersome to type.
This can be simplified by defining new commands in ~/.vimrc.
command -range=% C :<line1>,<line2>s/^/#/ command -range=% D :<line1>,<line2>s/^#//
And you can just type
:6,8C :6,8D
to place/delete the command.
If you like visual mode, you can define maps
map <F7> :s/^/#/<CR> map <F8> :s/^#//<CR>
Such that you only have to select a line range in visual mode, and press F7 and F8 to put and remove comments respectively.
Method 8
This answer is here to 1) show the correct code to paste into a .vimrc to get vim 7.4+ to do block commenting/uncommenting while keeping indentation level with 1 shortcut in visual mode and 2) to explain it.
Here is the code:
let b:commentChar='//'
autocmd BufNewFile,BufReadPost *.[ch] let b:commentChar='//'
autocmd BufNewFile,BufReadPost *.cpp let b:commentChar='//'
autocmd BufNewFile,BufReadPost *.py let b:commentChar='#'
autocmd BufNewFile,BufReadPost *.*sh let b:commentChar='#'
function! Docomment ()
"make comments on all the lines we've grabbed
execute '''<,''>s/^s*/&'.escape(b:commentChar, '/').' /e'
endfunction
function! Uncomment ()
"uncomment on all our lines
execute '''<,''>s/v(^s*)'.escape(b:commentChar, '/').'vs*/1/e'
endfunction
function! Comment ()
"does the first line begin with a comment?
let l:line=getpos("'<")[1]
"if there's a match
if match(getline(l:line), '^s*'.b:commentChar)>-1
call Uncomment()
else
call Docomment()
endif
endfunction
vnoremap <silent> <C-r> :<C-u>call Comment()<cr><cr>
How it works:
-
let b:commentChar='//': This creates a variable in vim. thebhere refers to the scope, which in this case is contained to the buffer, meaning the currently opened file. Your comment characters are strings and need to be wrapped in quotes, the quotes are not part of what will be substituted in when toggling comments. -
autocmd BufNewFile,BufReadPost *...: Autocommands trigger on different things, in this case, these are triggering when a new file or the read file ends with a certain extension. Once triggered, the execute the following command, which allows us to change thecommentChardepending on filetype. There are other ways to do this, but they are more confusing to novices (like me). -
function! Docomment(): Functions are declared by starting withfunctionand ending withendfunction. Functions must start with a capital. the!ensures that this function overwrites any previous functions defined asDocomment()with this version ofDocomment(). Without the!, I had errors, but that might be because I was defining new functions through the vim command line. -
execute '''<,''>s/^s*/&'.escape(b:commentChar, '/').' /e': Execute calls a command. In this case, we are executingsubstitute, which can take a range (by default this is the current line) such as%for the whole buffer or'<,'>for the highlighted section.^s*is regex to match the start of a line followed by any amount of whitespace, which is then appended to (due to&). The.here is used for string concatenation, sinceescape()can’t be wrapped in quotes.escape()allows you to escape character incommentCharthat matches the arguments (in this case,and/) by prepending them with a. After this, we concatenate again with the end of oursubstitutestring, which has theeflag. This flag lets us fail silently, meaning that if we do not find a match on a given line, we won’t yell about it. As a whole, this line lets us put a comment character followed by a space just before the first text, meaning we keep our indentation level. -
execute '''<,''>s/v(^s*)'.escape(b:commentChar, '/').'vs*/1/e': This is similar to our last huge long command. Unique to this one, we havev, which makes sure that we don’t have to escape our(), and1, which refers to the group we made with our(). Basically, we’re matching a line that starts with any amount of whitespace and then our comment character followed by any amount of whitespace, and we are only keeping the first set of whitespace. Again,elets us fail silently if we don’t have a comment character on that line. -
let l:line=getpos("'<")[1]: this sets a variable much like we did with our comment character, butlrefers to the local scope (local to this function).getpos()gets the position of, in this case, the start of our highlighting, and the[1]means we only care about the line number, not other things like the column number. -
if match(getline(l:line), '^s*'.b:commentChar)>-1: you know howifworks.match()checks if the first thing contains the second thing, so we grab the line that we started our highlighting on, and check if it starts with whitespace followed by our comment character.match()returns the index where this is true, and-1if no matches were found. Sinceifevaluates all nonzero numbers to be true, we have to compare our output to see if it’s greater than -1. Comparison invimreturns 0 if false and 1 if true, which is whatifwants to see to evaluate correctly. -
vnoremap <silent> <C-r> :<C-u>call Comment()<cr><cr>:vnoremapmeans map the following command in visual mode, but don’t map it recursively (meaning don’t change any other commands that might use in other ways). Basically, if you’re a vim novice, always usenoremapto make sure you don’t break things.<silent>means “I don’t want your words, just your actions” and tells it not to print anything to the command line.<C-r>is the thing we’re mapping, which is ctrl+r in this case (note that you can still use C-r normally for “redo” in normal mode with this mapping).C-uis kinda confusing, but basically it makes sure you don’t lose track of your visual highlighting (according to this answer it makes your command start with'<,'>which is what we want).callhere just tells vim to execute the function we named, and<cr>refers to hitting theenterbutton. We have to hit it once to actually call the function (otherwise we’ve just typedcall function()on the command line, and we have to hit it again to get our substitutes to go through all the way (not really sure why, but whatever).
Anyway, hopefully this helps. This will take anything highlighted with v, V, or C-v, check if the first line is commented, if yes, try to uncomment all highlighted lines, and if not, add an extra layer of comment characters to each line. This is my desired behavior; I did not just want it to toggle whether each line in the block was commented or not, so it works perfectly for me after asking multiple questions on the subject.
Method 9
There is this life changing plugin by tpope called vim-commentary
https://github.com/tpope/vim-commentary
This plugin provides:
- Sanity
- Properly indented comments
- Does not comment out empty/unnecessary lines
Usage:
- Install via Vundle (or Pathogen I guess).
- Highlight your text and press
:which will show as:<,'> - Type Commentary here
:<,'>Commentaryand press enter. - Bom. Your done bud.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0