0

I have 2 files which are lists containing test names. One file contains the base test name such as usb30_tb_7_10 and in the other file it is shown as 'include "usb30_tb_7_10.sv". I want to take all names from list` which are the base names (without include or .sv) and find them in list2 and comment out the entire line.

I.e.:

List 1

test1
test3

List 2 Before Comment

'include "test1.sv"
'include "test2.sv"
'include "test3.sv"
'include "test4.sv"

List 2 After compare

//'include "test1.sv"
'include "test2.sv"
//'include "test3.sv"
'include "test4.sv"

Im still new to grep/sed/awk, but I havent been able to find a solution to this yet.

I've seen answers involving awk which takes a specific pattern as the input, but that doesnt help me too much.

I tried to do the following:

awk '-include list1.lst {print "// " $0; next} 1' list2.sv

But it didnt work (or even accept the command since its typed incorrectly).

4

Using awk:

$ awk -F'[".]' 'NR==FNR{a[$0]; next} $2 in a{$0="//"$0} 1' list1 list2
//'include "test1.sv"
'include "test2.sv"
//'include "test3.sv"
'include "test4.sv"

How it works:

  • -F'[".]'

    This tells awk to separate fields whenever a " or . occurs.

  • NR==FNR{a[$0]; next}

    When reading the first file, list, this tells awk to create a key in associative array a equal the current line and then skip the rest of the commands and jump to the next line.

    More detail: NR is the total number of lines that awk has read so far. FNR is the number of lines read so far from the current file. This, when FNR==NR, we are reading the first file.

  • $2 in a{$0="//"$0}

    When reading the second file, this tells awk to add // to the beginning of the line if the second field in the current line is a key in associative array a.

  • 1

    This is awk's cryptic shorthand for print-the-line.

Extended example

Using the additional line mentioned in the comments:

$ cat list1
test1
test3
usb30_suspend_resume
$ cat list2
'include "test1.sv"
'include "test2.sv"
'include "test3.sv"
'include "usb30_suspend_resume.sv"
'include "test4.sv"
$ awk -F'[".]' 'NR==FNR{a[$0]; next} $2 in a{$0="//"$0} 1' list1 list2
//'include "test1.sv"
'include "test2.sv"
//'include "test3.sv"
//'include "usb30_suspend_resume.sv"
'include "test4.sv"

Adapting for pastebin files

The files on pastebin have trailing blanks in list1 and both files have windows-style line-endings. To handle this format, use:

awk 'NR==FNR{a[$1]; next} $2 in a{$0="//"$0} 1' FS='[[:space:]]' list1.txt FS='[".]' list2.txt
  • Hmm. It seems to be skipping some names. For example, I have `include "usb30_suspend_resume.sv" and usb30_suspend_resume as the pattern, but it doesnt comment out the first instance. But some of the others do get commented. – Javia1492 Aug 8 '17 at 13:29
  • @Javia1492 I updated files to include that line and, as you can see in the updated answer, it works for me. It could get confused, though, if there was one or more extraneous leading or trailing whitespaces in list1. – John1024 Aug 8 '17 at 17:54
  • Actually, this might be my mistake. It is not a 'include, but a `include. Does this change the command? – Javia1492 Aug 8 '17 at 18:24
  • That will make no difference: `include and 'include are treated the same. Could you upload somewhere for me to access the exact sample files that give you this problem? – John1024 Aug 8 '17 at 18:29
  • List1: pastebin.com/yZvVQStx List 2: pastebin.com/88j968n3 – Javia1492 Aug 8 '17 at 19:13
1

Frankly, I would do this interactively in Vim in less time than it would take to type any of the scripted solutions given so far.

vim list2

Then, read in list1 at the top. (I'm assuming it will be easy to tell visually where it ends and where list2 starts, so no need to mark anything ahead of time—but you can :1k a first if you like so the original line 1 of list2 is marked.)

:0r list1

After this command your cursor will be at the start of the file, on the first word test1.

Press * to go to next instance of word under cursor. (That will be the line you want to comment.)

Press I to enter insert mode (at the first non-whitespace character on the line, since you used a capital I and not lower case) and type // and then press Escape.

Press n to go to next instance of searched word. (Since you're at the start of the line just after your // characters, your cursor will go to the instance you just found. So press it again to go to the next instance.)

Assuming there was only the one instance to be commented, you'll now be at the start of the file again—the first line. Press j to go down (or just press Enter to go to the first non-whitespace character of the next line—same result in this case).

Press * to go to the next test3 instance, since that's what's under your cursor now.

Press . to repeat the "commenting out line" action. The dot command is awesome. :)

Press n again. (Twice.) In your example text, you're done now—you're back at the second line, test3. If there were more lines to be commented, again just type j*.nn. And if there is another, type j*.nn again.

When done (you're on test3 or whatever the last line was from list1, just above the original first line from list2), press dgg to delete all lines from the current line to the first line of the file—so that the list1 entries won't be there anymore.

In total, what you type is

vim list2<Enter>

to open the file, then:

:0r list1<Enter>
*I//<Esc>nn
j*.nn
j*.nn
j*.nn
(Repeat however many times)
dgg

Then to save and exit, type :x and press Enter.


EDIT:

I learned your "list1" is huge. Doesn't matter; just use a macro.

After doing the above a few times so you are sure it works, type:

qkj*.nnq

This will record j*.nn as a macro in register k.

Run the macro by typing @k.

Run it again by typing @@.

Then run it 4000 times by typing 4000@@. But personally, I would do it in smaller chunks. And perhaps not use the dot command in the macro but explicitly type I//<Esc>.

Point being, I would still do it interactively, and it would still take only a minute or two, regardless of how many lines I had to handle. The magic of Vim. :)

  • The file the OP pastebinned is almost 4000 lines. – NickD Aug 9 '17 at 2:42
  • @Nick, thanks, didn't realize that. Updated. – Wildcard Aug 9 '17 at 2:57
0

The algorithm is:

 for each line of List2
     if line matches ANY pattern in List1 then
         print //line
     else
         print line

Here's an implementation in bash (or similar):

 while read line ;do
     if echo $line | fgrep -f List1 >/dev/null ;then
         echo "//$line"
     else
         echo "$line"
     fi
 done < List2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.