1

I'm trying to match a string in my files which begins with imports: [ and does not contain SharedModule. It can have any number or spaces, line-breaks, or other characters (words) in between the two strings. I've been trying to do find those with:

grep 'imports: \[[.*\s*]*SharedModule' */*.module.ts

but I cannot even find the files which have 'SharedModule' in it. My thought process was that .* would find any words and \s would find blank space characters, and the character class with * selector would allow this to show up in any order.

  1. Can I use character classes like this to skip a variable number of unrelated lines/characters?
  2. How do I negate the statement so it returns lines without 'SharedModule'?
  3. The goal is to append 'SharedModule' to the import array wherever it does not already exist.

Thanks! (I'm new to this and the one thing I've learned so far is: regular expressions are hard)

SAMPLE MATCH:

imports: [
  IonicPageModule.forChild(FormPage),
  DynamicFormComponentModule,
  SharedModule
],

should not match but

imports: [
  IonicPageModule.forChild(LeadershipPage),
],

should.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
cjs
  • 190
  • 3
  • 15
  • 2
    `grep` is not multiline by default, check https://stackoverflow.com/a/7167115/6320039 – Ulysse BN Aug 14 '17 at 17:08
  • Am using options -Zo now, but still no results found as soon as I include any regular expressions. -P is not recognized? I'm on Mac OS X 10.11.6 – cjs Aug 14 '17 at 17:14

3 Answers3

1

grep doesn't process multiline strings by default. This is available with gnu grep with -z option but then regex will be a bit more complex.

You may be better off using gnu awk solution with custom RS (record separator):

awk -v RS='imports:[[:blank:]]*\\[[^]]*\\],[[:space:]]+' 'RT !~ /SharedModule/{ORS=RT} 1' file

imports: [
  IonicPageModule.forChild(LeadershipPage),
],

Where file content is this:

cat file
imports: [
  IonicPageModule.forChild(FormPage),
  DynamicFormComponentModule,
  SharedModule
],

imports: [
  IonicPageModule.forChild(LeadershipPage),
],
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • Great this works! It seems to be returning the whole file which matches instead of just the lines wanted. Will piping the results of this through to a sed command which adds the 'SharedModule' line work? – cjs Aug 14 '17 at 17:20
  • @ChrisSterbenz: What is your awk version? As I mentioned in my answer, it requires `gnu awk` – anubhava Aug 14 '17 at 18:00
  • On OSX, you can install `gnu awk` using `home brew` – anubhava Aug 14 '17 at 18:16
0

There is a solution with grep using Pzo option for multiline support and negative lookahead:

grep -Pzo 'imports: \[(?:(?!SharedModule)[^]])*]'

This will return import statements that are not containing SharedModule word.

Ulysse BN
  • 10,116
  • 7
  • 54
  • 82
0

You can simplify the regex requirements by using a little programming logic around them.

Here with POSIX awk:

$ awk '/\[/ {f=1} 
       f{s=s $0 ORS} 
       /\]/{if (index(s, "SharedModule")==0) print s; f=0; s=""}' file
imports: [
    IonicPageModule.forChild(LeadershipPage),
],

Explanation:

 /\[/ {f=1}             # if [ in line, set a flag
 f{s=s $0 ORS}          # if that flag is set, copy the input to the string s
 /\]/                   # closing ] in line
                        # print and reset
 {if ({if (index(s, "SharedModule")==0) print s; f=0; s=""}) print s; f=0; s=""}  

With this file:

$ cat file
imports: [
    IonicPageModule.forChild(FormPage),
    DynamicFormComponentModule,
    SharedModule
],

imports: [
    IonicPageModule.forChild(LeadershipPage),
],
dawg
  • 98,345
  • 23
  • 131
  • 206