Substitute in Vim doesn't seem to work globally? - vim

The global option in my substitute doesn't seem to work. Even when I do substitute with g set it substitutes the text only in the current line. If I run it again while staying on the current line which doesn't have any more matches it gives me
E486: Pattern not found: {patt}
If I go to the next match and run it again it works fine for that line. But there is literally no difference in using g and not using it.
Any ideas on what's wrong?
printf("abc");
printf("def");
Doing :s/printf/print/g highlights both the printf's but only the replaces the first one. I need to do n to go the next match and rerun substitute.

The g flag is to substitute the pattern multiple times per line.
What you're looking for is running the s/// command for the entire buffer:
:%s/pattern/replacement/g
Notice the % in front of s///, to select the entire buffer. It could also be a range of lines like this:
:10,21s/pattern/replacement/g
to perform the replacement between lines 10 and 21.
Without the g flag there, only the first occurrence of pattern will be replaced per line.

Related

Search and delete to end of line in vim

I am trying to clean up some code and I am trying to find a good way of achieving the following:
I am a #decent
guy
and I want:
I am a guy
I tried using
:g/#/d
but the whole line gets deleted and I only want to delete until the end of line. What is the best way to achieve this in vim?
Thank you.
That won't because the usage of that command:
:[range]g/pattern/cmd
defaults to range being the whole line, and you are not doing any substitution anyway.
Use:
:%s/#.\+\n//g
instead.
# Matches a literal #.
.\+\n Matches everything until the end of line, and a new line.
// Replaces the entire match with nothing.
With :global you would want something like
:global/#/normal! f#D | join
or
:global/#/substitute/#.*// | join
Try this instead:
:s/ # .*\n/ /
Explanation:
You were using the wrong command, as they may look similar to new users.
:[range]g/VRE/act Globally apply the "act"ion (one letter command) to all lines (in range, default all file) matching the VRE (pattern)
:[range]s/VRE/repl/f Substitute within lines (in range, default current line) the matching VRE (pattern) with the "repl"acement using optional "f"lags
Now about the pattern, I think this candidate cover most cases (all but comments at the beginning of a line and comments without space after pound sign)
# litteral space, then hash tag, then space again
.* dot for any character, star to mean the previous may occur many times or even be absent
$ dollar at end to stay at "end of line", but \n to catch en EOL here
press d + shift 4 or d + $, which means delete to end of the line
d means delete
shift 4 or $ means cursor to end of the line

How to find and remove part of word in vim?

I'm new into vim, I have hug text file as follow:
ZK792.6,ZK792.6(let-60),cel-miR-62(18),0.239
UTR3,IV:11688688-11688716,0.0670782
ZC449.3b,ZC449.3(ZC449.3),cel-miR-62(18),0.514
UTR3,X:5020692-5020720,0.355907
First, I would like to get delete all rows with even numbers (2,4,6...).
Second, I would like to remove (18) from entire file. as a example:
cel-miR-62(18) would be cel-miR-62.
Third: How can I get delete all parentheses including it's inside?
Would someone help me with this?
For the first one:
:g/[02468]\>/d
where :g matches all lines by the regex between the slashes and runs d (delete line) on the matching lines. The regex is quite easy to read, the only interesting symbol there is perhaps the \>, which matches end of a word.
For the second question:
:%s/\V(18)//g
where % is the specification meaning "all lines of the file", s is the substitute command, \V sets the "very nomagic" mode of regexes (not sure what your default is, you might not need this) and the final g makes vim substitute all occurrences on each line (with an empty string, the one between slashes). Make sure that :set gdefault? prints nogdefault (the default setting of gdefault), otherwise, drop the final g from the substitute command.
To remove every even line (or every other line):
:g/^/+d
To remove every instance of (18):
:%s/(18)//g
Remove all the parenthetical content:
:%s/(.\\{-})//g
Note: the pattern in third answer is a non-greedy match.

Vim - Delete til last occurrence of character in line

I'm trying to figure out how to dt or df the last occurrence of a character in a string.
For example, let's say I have the following line:
foo not.relevant.text.bar
If I f df. I expectedly get foo relevant.text.bar but I would like to get foo bar. Using f 3df. is not an option as I don't know how many of that character will be in the string. Additionally, I may want to get foo .bar (f 3dt.), or if the line ends with a dot, I may want to get foo .. I want to always find the last one regardless of how many there are.
Is this possible without a regex? I suppose I could use a regex but I was hoping there was a simple vim command that I'm missing. I find myself trying to do something like this often.
one way without using regex, without counting "dot" (could be other letters)... see if others have better way..
foo[I]not.relevant.text.bar ([I] is cursor)
you could try:
lmm$T.d`m
or in this format, may look better?
lmm$T.d`m
this will do the job. you could create a mapping if you use that often.
EDIT
I add a GIF animation to show it works. :)
note
I typed #= in normal mode after moving my cursor to the right starting point (by f(space)), to display the keys I pressed in command line.
You can use my JumpToLastOccurrence plugin. It provides ,f / ,F / ,t / ,T commands that do just that.
I would use f df...
It is not necessarily shorter to type, but I find it easier to use "repeat last command" than counting in advance the number of word/sentence I want to delete.
Then you can adjust the number of . you type to adjust the length of the string you want to delete.
For your example: ET.dB
foo not.relevant.text.bar
And it works, as long as the cursor is anywhere within the text following "foo".
Strip Path from Path+Filename: ET/dB
I use it for stripping a pathname of all but the trailing filename.
Strip the path from /some/long/path/filename.ext leaving only the filename.
Just as long as:
The cursor is anywhere within the bold word
There are no spaces in that word
E Go to the end (since there are no spaces - also works if not the last thing on the line)
T/ Find the last / (stop just after it, so it will be deleted, as well)
dB Delete to the beginning of the word
In visual mode:
$F.d^
The $ goes to the end of the current line, F searches backward for a period and d^ deletes till the beginning of the line.

Vim: substitution in a range that is less than a line

Let's say I have the following line of code:
something:somethingElse:anotherThing:woahYetAnotherThing
And I want to replace each : with a ; except the first one, such that the line looks like this:
something:somethingElse;anotherThing;woahYetAnotherThing
Is there a way to do this with the :[range]s/[search]/[replace]/[options] command without using the c option to confirm each replace operation?
As far as I can tell, the smallest range that s acts on is a single line. If this is true, then what is the fastest way to do the above task?
I'm fairly new to vim myself; I think you're right about range being lines-only (not 100% certain), but for this specific example you might try replacing all of the instances with a global flag, and then putting back the first one by omitting the global -- something like :s/:/;/g|s/;/:/.
Note: if the line contains a ; before the first : then this will not work.
Here you go...
:%s/\(:.*\):/\1;/|&|&|&|&
This is a simple regex substitute that takes care of one single not-the-first :.
The & command repeats the last substitute.
The | syntax separates multiple commands on one line. So, each substitute is repeated as many times as there are |& things.
Here is how you could use a single keystroke to do what you want (by mapping capital Q):
map Q :s/:/;/g\|:s/;/:<Enter>j
Every time you press Q the current line will be modified and the cursor will move to the next line.
In other words, you could just keep hitting Q multiple times to edit each successive line.
Explanation:
This will operate globally on the current line:
:s/:/;/g
This will switch the first semi-colon back to a colon:
:s/;/:
The answer by #AlliedEnvy combines these into one statement.
My map command assigns #AlliedEnvy's answer to the capital Q character.
Another approach (what I would probably do if I only had to do this once):
f:;r;;.
Then you can repeatedly press ;. until you reach the end of the line.
(Your choice to replace a semi-colon makes this somewhat comfusing)
Explanation:
f: - go to the first colon
; - go to the next colon (repeat in-line search)
r; - replace the current character with a semi-colon
; - repeat the last in-line search (again)
. - repeat the last command (replace current character with a semi-colon)
Long story short:
fx - moves to the next occurrence of x on the current line
; repeats the last inline search
While the other answers work well for this particular case, here's a more general solution:
Create a visual selection starting from the second element to the end of the line. Then, limit the substitution to the visual area by including \%V:
:'<,'>s/\%V:/;/g
Alternatively, you can use the vis.vim plugin
:'<,'>B s/:/;/g

Vim: delete until character for all lines containing a pattern

I'm learning the power of g and want to delete all lines containing an expression, to the end of the sentence (marked by a period). Like so:
There was a little sheep. The sheep was black. There was another sheep.
(Run command to find all sentences like There was and delete to the next period).
The sheep was black.
I've tried:
:g/There was/d\/\. in an attempt to "delete forward until the next period" but I get a trailing characters error.
:g/There was/df. but get a df. is not an editor command error.
Any thoughts?
The action associated with g must be able to act on the line without needing position information from the pattern match that g implies. In the command you are using, the delete forward command needs a starting position that is not being provided.
The problem is that g only indicates a line match, not a specific character position for it's pattern match. I did the following and it did what I think you want:
:g/There was/s/There was[^.]*[.]//
This found lines that matched the pattern There was, and performed a substitution of the regular expression There was[^.]*[.] with the empty string.
This is equivalent to:
:1,$s/There was[^.]*[.]//g
I'm not sure what the g is getting you in your use case, except the automatic application to the entire file line range (same as 1,$ or %). The g in this latter example has to do with applying the substitution to all patterns on the same line, not with the range of lines affected by the substitution command.
I'd just use a regex:
%s/There was\_.\{-}\.\s\?//ge
Note how \_. allows for cross-line sentences
You can use :norm like this:
:g/There was/norm 0weldf.
This finds lines with "There was" then executes the normal commands 0weldf..
0: go to beginning of line
w: go to next word (in this case, "was")
e: go the end of the word (so cursor is on the 's' of "was")
l: move one character to the right (so we don't delete any of "was")
df.: delete until the next '.', inclusive.
If you want to keep the period use dt. instead of df..
If you don't want to delete from the beginning of the line and instead want to do sentences, the :%s command is probably more appropriate here. (e.g. :%s/\(There was\)[^.]*\./\1/g or %s/\(There was\)[^.]*\./\1./g if you want to keep the period at the end of the sentence.
Use search and replace:
:%s/There was[^.]*\.\s*//g

Resources