How to search and substitute inside Vim? [duplicate] - linux

This question already has answers here:
How can I replace a pattern only on lines that do or do not contain another pattern?
(2 answers)
Closed 1 year ago.
Is there a way to specifically search for a pattern first and then replace using %s
Why I specifically chose vim is because I can easily make change using :wq
Consider I got a file abc.txt where I need to replace all two's with three:
cat abc.txt
one two
two two
one two
But I only need to replace only those lines that is having "one".
Is there a way to first grep or filter lines having "one" inside vim and then replace using
:%s/two/three/g.
I need a one-liner command that can filter out all lines having "one" and perform substitution.
Sample output
cat abc.txt
one three
two two
one three
Many thanks in advance!

Use a Capture Group or a Look Behind Pattern to Solve Substitution
This ex command should meet your needs:
:`%s/\(one\) two/\1 three/
This is also a classic look behind pattern matching problem (:help /#<=).
:%s/\(one\)\#<= two/ three/
Another approach might be to use \zs, \ze pattern matching technique. Look up :help \zs.
%s/one \zstwo\ze/three/

Related

Remove until end of the line after the same character from different locations in multiple lines [duplicate]

This question already has answers here:
Delete all characters after "." in each line
(5 answers)
Closed 17 days ago.
I want to remove anything from : to the end of line in all of the following lines in Vim:
key1: A
key2_long: B
key3_longerrr: C
key: D
So the result would be:
key1
key2_long
key3_longerrr
key
For a single line I use f:d$. What are better ways to do this for all lines, wihtout repeating f:d$ for all lines?
:%s/:.*//g
It searches and replaces what matches the regex between the first and second / with what is between the second and third in all the file.
you can find more on this in the documentation, look for substitute
If you want to stick with f:d$ (which should be f:D), you can use :normal for a one-off macro:
:,+3normal f:D
or:
vjjj:normal f:D
See :help :range and :help :normal.

SED: insert a word/string between two patterns in the SAME LINE

I've searched all over stackoverflow (perhaps I just suck at searching) but I cannot find the answer to my problem. I'm trying to insert a word or a string in between two patterns in the same line using sed.
I know how to insert a word AFTER a searched pattern using
sed -e "s/pattern/& new_word/g"
with an ampersand (&).
But this command inserts 'new_word' in every occurrence of searched pattern so I'm trying to specify it so that it inserts 'new_word' only in between two patterns.
For example,
Some words = [want to insert words here];
How do I insert it between "Some words (multiple whitespaces here) =" and ";"?
What is the syntax for this kind of command? Also, what resources do you guys use to learn sed? Many of the sed tutorials that I've searched are very basic and doesn't go into details of usage of different options and flags.
Thank you.
Use capture groups.
sed -e 's/(pattern1)(pattern2)/\1new_word\2/'
\1 is replaced with whatever matched the first pattern, \2 gets whatever matched the second pattern.

Adding words in front and at the end of line in VIM [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Replace while keeping certain “words” in vi/vim
Let's say I have several lines like
$adv_id;
$am_name;
$campaign_ids;
$repeat_on;
$opt_days;
$opt_time;
$am_or_pm;
Let's say I use visual mode to select all the lines.. how can I add characters at the start and the end of each line so it looks something like
$_REQUEST($adv_id);
$_REQUEST($am_name;
$_REQUEST($campaign_ids);
$_REQUEST($repeat_on;
$_REQUEST($opt_days);
$_REQUEST($opt_time);
$_REQUEST($am_or_pm);
Pretty similar to your other question, so the explanation there should help you to understand this substitute. With the lines selected as a visual block, use this substitute command:
:'<,'>s/\$\(.*\);/$_REQUEST(\1);
As before, the '<,'> will be auto-filled for you in the command-line if you have a visual selection.
The difference here is that we're using \(\) to make a capturing group, which will capture part of the regex, and use it again in the replacement, using \1, which refers to the first capturing group.
Also, since this regex uses $ literally to position the replacement, it needs to be escaped: \$, since it has a special meaning in the regex: end of line.
If you'll need to have multiple replacements on a single line, you'll need to add the g flag, and you may want to remove the semicolon:
:'<,'>s/\$\(.*\);/$_REQUEST(\1)/g
The reverse regex, e.g. replacing $_REQUEST($adv_id); with $adv_id;, is pretty similar:
:'<,'>s/\$_REQUEST(\(.*\))/\1
Here we capture everything between the parens in a $_REQUEST(...); in a capturing group, and that capturing group is the entire replacement.
In visual mode, hit : and use this in the command line:
:'<,'>s/^\(.*\);$/$_REQUEST(\1);/g
The \( and \) capture the matched expression for the line and the \1 recalls the captured group in the substitution.
Using the :'<,'> tells Vim to filter the current selection through the following command (which is s in this case).

Using Vim, how do you use a variable to store count of patterns found?

This question was helpful for getting a count of a certain pattern in Vim, but it would be useful to me to store the count and sum the results so I can echo a concise summary.
I'm teaching a class on basic HTML to some high schoolers, and I'm using this script to be quickly check numbers of required elements throughout all their pages without leaving Vim. It works fine, but when students have more than 10 .html files it gets cumbersome to add up the various sections by hand.
Something like:
img_sum = :bufdo %s/<img>//gen
would be nice. I think I'll write a ruby script to check the pages more thoroughly and check for structure, but for now I'm curious about how to do this in Vim.
The problem can be solved by a counter separate from the one built-in into the
:substitute command: Use Vim-script variable to hold the number of pattern
matches. A convenient way to register every match and modify a particular
variable accordingly, is to take advantage of the substitute with an
expression feature of the :substitute command (see :help sub-replace-\=).
The idea is to use a substitution that evaluates an expression increasing
a counter on every occurrence, and does not change the text it is operating
on.
The first part of the technique cannot be implemented straightforwardly
because it is forbidden to use Ex commands in expressions (including \=
substitute expressions), and therefore it is not possible to use the :let
command to modify a variable. Answering the question "gVim find/replace
with counter", I have proposed a simple trick to overcome that limitation,
which is based on using a single-item list (or dictionary containing a single
key-value pair). Since the map() function transforms a list or a dictionary
in place, that only item could be changed in a constrained expression context.
To do that, one should call the map() function passing an expression
evaluating to the new value along with the list containing the current value.
The second half of the technique is how to avoid changing text when using
a substitution command. In order to achieve that, one can make the pattern
have zero-width by prepending \ze or by appending \zs atoms to it (see
:help /\zs, :help /\ze). In such a way, the modified pattern captures
a string of zero width just before or after the occurrence of the initial
pattern. So, if the replacement text is also empty, substitution does not
cause any change in the contents of a buffer. To make the substitute
expression evaluate to an empty string, one can just extract an empty
substring or sublist from the resulting value of that expression.
The two ideas are put into action in the following command.
:let n=[0] | bufdo %s/pattern\zs/\=map(n,'v:val+1')[1:]/ge
I think that answer above is hard to understand and more pretty way to use external command grep like this:
:let found=0
:bufdo let found=found+(system('grep "<p>" '.expand('%:p') . '| wc -l'))
:echo found

How to delete text in a file based on regular expression using vim

I have an XML file like this:
<fruit><apple>100</apple><banana>200</banana></fruit>
<fruit><apple>150</apple><banana>250</banana></fruit>
Now I want delete all the text in the file except the words in tag apple. That is, the file should contain:
100
150
How can I achive this?
:%s/.*apple>\(.*\)<\/apple.*/\1/
That should do what you need. Worked for me.
Basically just grabbing everything up to and including the tag, then backreferences everything between the apple begin and end tag, and matches to the rest of the line. Replaces it with the first backreference, which was the stuff between the apple tags.
I personally use this:
%s;.*<apple>\(\d*\)</apple>.*;\1;
Since the text contain '/' which is the default seperator,and by using ';' as sep makes the code clearer.
And I found that non-greedy match #Conspicuous Compiler mentioned should be
\{-}
instead of "{-}" in Vim.
However, I after change Conspicuous' solution to
%s/.*apple>(.\{-\})<\/apple.*/\1^M/g
my Vim said it can't find the pattern.
In this case, one can use the general technique for collecting pattern matches
explained in my answer to the question "How to extract regex matches
using Vim".
In order to collect and store all of the matches in a list, run the Ex command
:let t=[] | %s/<apple>\(.\{-}\)<\/apple>\zs/\=add(t,submatch(1))[1:0]/g
The command purposely does not change the buffer's contents, only collects the
matched text. To set the contents of the current buffer to the
newline-separated list of matches, use the command
:0pu=t | +,$d_

Resources