I search for a particular string in a file in vim, and I want all the lines with matching string to be displayed, perhaps in another vim window.
Currently I do this:
Search for 'string'
/string
and move to next matching string
n or N
Bur, I want all the lines with matching string at one place.
For example:
1 Here is a string
2 Nothing here
3 Here is the same string
I want lines 1 and 3 to be displayed as below, highlighting string
1 Here is a string
3 Here is the same string
:g/pattern/#<CR>
lists all the lines matching pattern. You can then do :23<CR> to jump to line 23.
:ilist pattern<CR>
is an alternative that filters out comments and works across includes.
The command below:
:vimgrep pattern %|cwindow<CR>
will use Vim's built-in grep-like functionality to search for pattern in the current file (%) and display the results in the quickfix window.
:grep pattern %|cwindow<CR>
does the same but uses an external program. Note that :grep and :vimgrep work with files, not buffers.
Reference:
:help :g
:help include-search
:help :vimgrep
:help :grep
:help :cwindow
FWIW, my plugin vim-qlist combines the features of :ilist and the quickfix window.
From the comments I believe your file looks like this, i.e. the line numbers are not part of the text:
Here is a string
Nothing here
Here is the same string
You could copy all lines matching a pattern into a named register ("a" in the example below), then paste it into a new file:
:g/string/y A
:e newfile
:"ap
Which gets you:
Here is a string
Here is the same string
Alternatively, you can use the grep command and add -n to include line numbers:
:grep -n string %
1:~/tmp.txt [text] line: 3 of 3, col: 23 (All)
:!grep -nH -n string /home/christofer/tmp.txt 2>&1| tee /tmp/vHg7GcV/3
[No write since last change]
/home/christofer/tmp.txt:1:Here is a string
/home/christofer/tmp.txt:3:Here is the same string
(1 of 2): Here is a string
Press ENTER or type command to continue
By default you'll get the output in the "command buffer" down at the bottom (don't know its proper name), but you can store it in several different places, using :copen for example.
Following this answer over on the Vi StackExchange:
:v/mystring/d
This will remove all lines not containing mystring and will highlight mystring in the remaining lines.
Related
Is it possible with notepad++ (or maybe from linux bash shell) to create multiple lines from a pattern found , as many times as the pattern is found and also append single found pattern in the newly created line?
The multi pattern is val=[0-9]+
The single pattern is id=[a-zA-Z0-9]+
Example:
Input lines:
id=af2477,val=333,val=777
id=af3456,val=222,val=444,val=678
id=af3327,val=3234,val=123,val=701
Output lines:
id=af2477,val=333
id=af2477,val=777
id=af3456,val=222
id=af3456,val=444
id=af3456,val=678
id=af3327,val=3234
id=af3327,val=123
id=af3327,val=701
I have tried with 2 subgroups but it wont work. It will only replace the second group once:
find what:(id=[a-zA-Z0-9]+,)(val=[0-9]+,)*
replace:\n\1,\2
UPDATE: Both answers from Toto and Wiktor Stribiżew seem to do the job. Haven't tested them yet. I would still like to see how this can work with the use of Notepad++ (even if multiple steps are needed)
Since you also consider using Linux tools for this, an awk solution looks much more viable:
awk 'BEGIN{FS=OFS=","} /^id=[a-zA-Z0-9]+(,val=[0-9]+)*$/{
for(i=2; i<=NF; i++) {
print $1,$i
}; next;
}{print $0}' file > outfile
See the online demo.
Here, any line that matches ^id=[a-zA-Z0-9]+(,val=[0-9]+)*$ (i.e. matches the format of the lines you need to expand) is split the way you need with for(i=2; i<=NF; i++) {print $1,$i}; next;. Else, the line is written as is (print $0).
The BEGIN{FS=OFS=","} part sets the input and output field separator to a comma.
This perl one-liner does the job (output on STDOUT):
perl -anE '($id,$vals)=/(id=\w+),(.+)$/;say "$id,$_" for split/,/,$vals' file
id=af2477,val=333
id=af2477,val=777
id=af3456,val=222
id=af3456,val=444
id=af3456,val=678
id=af3327,val=3234
id=af3327,val=123
id=af3327,val=701
Explanation:
($id,$vals)=/(id=\w+),(.+)$/; # explode id and values for each line in input file
say "$id,$_" for split/,/,$vals # print id and each value
You can redirect the output to another file:
perl -anE '($id,$vals)=/(id=\w+),(.+)$/;say "$id,$_" for split/,/,$vals' file > outputfile
Or do the change in-place:
perl -i -anE '($id,$vals)=/(id=\w+),(.+)$/;say "$id,$_" for split/,/,$vals' file
It is possible, yet very complex to do that with one regular expression for which you are gonna have to use (?R) and conditional statements.
With multiple steps would be pretty simple. You can for instance do find and replace using the max number of val that you might have in the longest lines, such as, imagine 4 would be the largest number of val, then we'll have four of (,val=[^\r\n,]*) in our initial expression:
^(id=[^\r\n,]*)(,val=[^\r\n,]*)(,val=[^\r\n,]*)(,val=[^\r\n,]*)(,val=[^\r\n,]*)$
and replace that with four lines,
$1$2\n$1$3\n$1$4\n$1$5
---- ---- ---- ----
Demo for Step 1
For any additional step, we can simply remove one val and one line from the end of initial expression and replacement. For example, our expression would look like
^(id=[^\r\n,]*)(,val=[^\r\n,]*)(,val=[^\r\n,]*)(,val=[^\r\n,]*)$
in the second step, for which we'd replace it with:
$1$2\n$1$3\n$1$4
---- ---- ----
Demo for Step 2
In the third and final step, our expression has two vals,
^(id=[^\r\n,]*)(,val=[^\r\n,]*)(,val=[^\r\n,]*)$
and our replacement will have two lines:
$1$2\n$1$3
---- ----
Demo for Step 3
For the case exampled in the question, only two steps are required and the second and third expressions would likely work just fine.
I have a directory with a bunch of text files, all of which follow this structure:
...
- Some random number of list items of random text
- And even more of it
PATTERN_A (surrounded by empty lines)
- Again, some list items of random text
- Which does look similar as the first batch
PATTERN_B (surrounded by empty lines)
- And even more some random text
....
And I need to run a replace operation (let's say, I need to prepend CCC at the beginning of the line, just after the dash) on only those "list items", which are between PATTERN_A and PATTERN_B. The problem is they aren't really much different from the text above PATTERN_A, or below PATTERN_B, so an ordinary regex can't really catch them without also affecting the remaining text.
So, my question would be, what tool and what regex should I use to perform that replacement?
(Just in case, I'm fine with Vim, and I can collect those files in a QuickFix for a further :cdo, for example. I'm not that good with awk, unfortunately, and absolutely bad with Perl :))
Thanks!
If I have understood your questions, you can do so quite easily with a pattern-range selection and the general substitution form with sed (stream editor). For example, in your case:
$ sed '/PATTERN_A/,/PATTERN_B/s/^\([ ]*-\)/\1CCC/' file
- Some random number of list items of random text
- And even more of it
PATTERN_A (surrounded by empty lines)
-CCC Again, some list items of random text
-CCC Which does look similar as the first batch
PATTERN_B (surrounded by empty lines)
- And even more some random text
(note: to substitute in place within the file add the -i option, and to create a backup of the original add -i.bak which will save the original file as file.bak)
Explanation
/PATTERN_A/,/PATTERN_B/ - select lines between PATTERN_A and PATTERN_B
s/^\([ ]*-\)/\1CCC/ - substitute (general form 's/find/replace/') where find is from beginning of line ^ capturing text between \(...\) that contains [ ]*- (any number of spaces and a hyphen) and then replace with \1 (called a backreference that contains all characters you captured with the capture group \(...\)) and appending CCC to its end.
Look things over and let me know if you have questions or if I misinterpreted your question.
With Perl also, you can get the results
> perl -pe ' { s/^(\s*-)/\1CCC/g if /PATTERN_A/../PATTERN_B/ } ' mass_replace.txt
...
- Some random number of list items of random text
- And even more of it
PATTERN_A (surrounded by empty lines)
-CCC Again, some list items of random text
-CCC Which does look similar as the first batch
PATTERN_B (surrounded by empty lines)
- And even more some random text
....
>
I have a text file that looks like the following:
Chanelle
Jettie
Winnie
Jen
Shella
Krysta
Tish
Monika
Lynwood
Danae
2649
2466
2890
2224
2829
2427
2816
2648
2833
2453
I need to make it look like this
Chanelle 2649
Jettie 2466
... ...
I tried a lot on sublime editor but couldn't figure out the regex to do that. Can somebody demonstrate if it can be done.
I tested the following in Notepad++ but it should work universally.
Use this as the search string:
(?:(\s+[A-Za-z]+)(\r?\n))((?:\s*[A-Za-z]*\r?\n)+)\s+(\d+)
and this as the replacement:
$1 $4$2$3
Running a replace with it once will do one line at a time, if you run it multiple times it'll continue to replace lines until there are no matching lines left.
Alternatively, you can use this as the replacement if you want to have the values aligned by tabs, but it's not going to match in all cases:
$1\t\t$4$2$3
While the regex answer by SeinopSys will work, you don't need a regex to do this - instead, you can take advantage of Sublime's multiple cursors.
Place your cursor at the beginning of line 1, then hold down Shift↓ to select all the names.
Hit CtrlShiftL (Selection -> Split into Lines) to split the selection into lines.
CtrlC to copy.
Place your cursor on line 11 (the first number line) and press CtrlShift↓ (Windows/OS X) or AltShift↓ (Linux) to place a cursor at the beginning of each number line.
Hit CtrlV to paste the names before the numbers.
You can now delete the names at the top and you're all set. Alternatively, you could use CtrlX to cut the names in step 3.
I am using vim to manage a to-do list, and would like to set a hotkey to do the following:
Find all lines which contain the word under the cursor. The world is always a tag formatted as: #mytaghere
Sort all of these lines alphabetically by another tag contained within them. Again the tag is in #mytaghere format. For example, all lines with the tag #priorityA would be alphabetized and placed above all lines with the tag #priorityB, and so on.
Put all of these lines into Vim's quickfix window.
I have managed to achieve a limited version of 1 and 3 by putting the following into my .vimrc file:
map <f2> :vimgrep <cword> % <bar> copen <enter>
set iskeyword=#,48-57,_,192-255,95,35 "lets cword include #s and _s
This setup fails whenever the word under the cursor contains a #, and it works whenever the character is something else (for example, an _ ) The error produced is: "E682: Invalid search pattern or delimiter" which seems like the # is interfering with vimgrep.
Also, I can't figure out how to sort the lines by priority tags.
Thanks in advance for any help. I am just learning Vim, and if I could just get this problem solved I'd be able to use it for all of my organizing.
The :vimgrep command comes in two forms:
:vim[grep][!] /{pattern}/[g][j] {file} ...
:vim[grep][!] {pattern} {file} ...
For /{pattern}/, actually any non-keyword delimiter can be used. When your <cword> starts with #, Vim interprets this as the start delimiter, then complains because there's no end delimiter.
Choose a delimiter and put it around <cword>; to make the search literal, use \V and escape the current word, which is now inserted though the expression register =:
:noremap <f2> :vimgrep /<C-r>='\V'.escape(expand('<cword>'), '/\')<CR>/ % <bar> copen <enter>
Oh, and you should use :noremap; it makes the mapping immune to remapping and recursion.
Thanks to Ingo Karkat I was able to come up with the following solution:
noremap <f2> :vimgrep /<C-r>='\V'.escape(expand('<cword>'), '/\')<CR>/ % <bar> copen <enter>
The above will take everything under the cursor and put it into the quickfix window.
:set modifiable
:sort /.*|.*|/
The above will sort everything in the quick fix window ignoring the file name and line/column information.
To do everything at once (find all tagged lines and sort them by the first character of those lines):
noremap <f2> :vimgrep /<C-r>='\V'.escape(expand('<cword>'), '/\')<CR>/ % <bar> copen <enter> <bar> :set modifiable <enter> <bar> :sort /.*\|.*\|/ <enter>
I'm trying to formulate a regular expression that matches the names of a set of files I would like to batch process in Vim but am finding that I cannot seem to use \| (regex OR) as expected...
Specifically, I would like to create an argument list consisting of the following files in the current directory:
f0148.e, f0149.e, f0150.e ... f0159.e (i.e., 12 files total)
The vim command I entered goes as follows:
:arg f01\(\(4[89]\)\|\(5[0-9]\)\).e
Vim completes this command without any noticeable result -- there's no message and the output from :args remains unchanged (doesn't produce the desired list of file names).
If I split up the regular expression to:
:arg f01\(\(4[89]\)\).e (note: leaving parenthesis here as in above full expression)
...and...
:arg f01\(\(5[0-9]\)\).e
... then :args produces f0148.e f0149.e and f0150.e ... f0159.e respectively (as desired).
Also, if I enter the above mentioned list of file names in a text file and use the above mentioned regular expression as a search pattern (i.e., /f01\(\(4[89]\)\|\(5[0-9]\)\).e), it works just as desired.
Thus, I determined that the alternation (\|) is somehow causing the the expression to fail. Please note that I'm using Vim on Windows 7, if this is relevant (since both backslash and pipe are valid symbols at the Windows command prompt).
A quick workaround would be to use:
:arg f014[89].e
:argadd f015[0-9].e
...but I would really like to figure out how to make the above regular expression work.
Thanks for your help!
I could suggest:
:let file_list = filter(split(globpath('.','**'),nr2char(10)), 'v:val =~ ''f01\(\(4[89]\)\|\(5[0-9]\)\)\.e'' ')
:execute 'args ' . join(map(file_list,'fnameescape(v:val)'),' ')
How this works:
globpath('.','**') makes a list of all files in current directory and all subdirectories. :help globpath().
split(..., nr2char(10)) will make a list of it, because the separator was Line Feed
filter(..., 'v:val =~ ''pattern'' ') filters the list keeping only items matching pattern. :help v:val. Doubling single quote is escaping them inside single quote string.
map(..., fnameescape()) escapes all spaces and backslashes
join() adds spaces between file names
If you want to make it a function you can put this into your vimrc:
function! ArgsPattern(pat)
let file_list = filter(split(globpath('.','**'),nr2char(10)), 'v:val =~ ''' . substitute(a:pat,"'","''",'g') . '''')
execute 'args ' . join(map(file_list,'fnameescape(v:val)'),' ')
endfunction
command! -nargs=+ ArgsPattern call ArgsPattern(<q-args>)
And then you only have to do:
:ArgsPattern f01\(\(4[89]\)\|\(5[0-9]\)\)\.e
Note that if there is no match, then the execute command inside the function evaluates to :args and therefore the list of your current arguments are printed.