0

for eg: replace com.icc.id with "abc" if n+2 line (version line contains com.icc.id.version). If it is something else, do not replace. This should be applicable to all pom files present in all sub-directories for a folder "common"

<groupId>com.icc.id</groupId>
<artifactId>abc</artifactId>
<version>${project.version}</version>
<groupId>com.icc.id</groupId>
<artifactId>ifd</artifactId>
<version>${com.icc.id.version}</version>
  • @user312307 please remember to accept whichever answer you choose by clicking on the checkmark on the left. That will mark the question as answered and is the way that thanks are conveyed on the Stack Exchange sites. – terdon Jun 16 at 18:21
  • @terdon Thanks for your response, Suppose if the condition is that i+2 line should not contain /com\.icc\.id\.version/ then replace. I tried with !~, it did not work. Any suggestions? – user312307 Jun 18 at 19:22
0

Using the original xml example (the tags needed fixing first as dependencies wasn't closed) and using xmlstarlet

xmlstarlet ed -N "x=http://maven.apache.org/POM/4.0.0" -u "//x:dependency[x:version[contains(text(), 'com.icc.version')]]/x:groupId" -v "BANANA" data.xml

Edit

Wrt the dual constraint that changes are made to groupId only where version contains com.icc.version and groupId is not com.icc.id then the following should only select and replace those nodes

xmlstarlet ed -N "x=http://maven.apache.org/POM/4.0.0" -u "//x:dependency[x:version[contains(text(), 'com.icc.version')]]/x:groupId[not(contains(text(), 'com.icc.id'))]" -v 'BANANA' data.xml

or if the entire text of the groupID node is com.icc.id then

xmlstarlet ed -N "x=http://maven.apache.org/POM/4.0.0" -u "//x:dependency[x:version[contains(text(), 'com.icc.version')]]/x:groupId[text() !='com.icc.id']" -v 'BANANA' data.xml

The globstar loop from @terdon will find the files and the output will go to stdout for you to check. To make the changes permanent in the .pom files then include the -L option in the command.

xmlstarlet ed -L -N  etc.....

Input

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <dependencies>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
        </dependency>
        <dependency>
            <groupId>com.icc.id</groupId>
            <artifactId>solution-commons</artifactId>
            <version>${com.icc.id.version}</version>
        </dependency>
        <dependency>
            <groupId>com.icc.banana</groupId>
            <artifactId>application-common</artifactId>
            <version>${com.icc.version}</version>
        </dependency>
        <dependency>
            <groupId>com.icc.id</groupId>
            <artifactId>application-common</artifactId>
            <version>${com.icc.banana}</version>
        </dependency>
    </dependencies>
</project>

Ouput

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <dependencies>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
    </dependency>
    <dependency>
      <groupId>com.icc.id</groupId>
      <artifactId>solution-commons</artifactId>
      <version>${com.icc.id.version}</version>
    </dependency>
    <dependency>
      <groupId>BANANA</groupId>
      <artifactId>application-common</artifactId>
      <version>${com.icc.version}</version>
    </dependency>
    <dependency>
      <groupId>com.icc.id</groupId>
      <artifactId>application-common</artifactId>
      <version>${com.icc.banana}</version>
    </dependency>
  </dependencies>
</project>

One warning on this. xmlstarlet relies upon knowing the namespace, so if the namespace is the same in all files then, no problem. If it is different then you will need to interrogate each file and determine the namespace and pass it in as a variable.

ns=$(grep -Po "(?<=xmlns=\")[^\"]*" data.xml)
  • Can I use this command in a shell script? – user312307 Jun 21 at 20:40
  • I tried this command but I also need to have a checkpoint for groupid that if it is abc and version is xyz then only replace. Current command is replacing if version is xyz even if group-id is not abc – user312307 Jun 21 at 21:04
  • Thanks bu5hman for this amazing solution, it works like magic – user312307 Jun 24 at 15:00
  • You are welcome. If you work with xml a lot then it is well worth using a proper xml parser like xmlstarlet and getting your head around xpath. A good free resource for xpath is w3 Schools if you need. – bu5hman Jun 24 at 15:30
  • Thanks, I appreciate – user312307 Jun 24 at 18:28
3

This is more than you need but it sets you up to be able to do a lot more with your records in future:

$ cat tst.awk
BEGIN { numLines = 3 }
{
    lineNr = (NR - 1) % numLines + 1
    rec[lineNr] = $0
}
lineNr == numLines {
    if ( rec[3] ~ /com\.icc\.id\.version/ ) {
        sub(/com\.icc\.id/,"abc",rec[1])
    }
    for (lineNr=1; lineNr<=numLines; lineNr++) {
        print rec[lineNr]
    }
    delete rec
}

$ awk -f tst.awk file
(groupId)com.icc.id(/groupId)
(artifactId)abc(/artifactId)
(version)${project.version}(/version)
(groupId)abc(/groupId)
(artifactId)ifd(/artifactId)
(version)${com.icc.id.version}(/version)

To do it with all ".pom" files in all sub-directories, assuming "pom files" have such an extension and using GNU awk for -i inplace:

find . -name '*.pom' -exec awk -i inplace -f tst.awk {} +
  • Thanks for your response, Suppose if the condition is that i+2 line should not contain /com\.icc\.id\.version/ then replace. I tried with !~, it did not work. Any suggestions? – user312307 Jun 19 at 14:17
  • You're welcome. You already accepted an answer so you should be asking followup questions of the person whose answer you accepted. – Ed Morton Jun 19 at 14:21
  • Yes, I have also posted the same question to that user yesterday, but have not got any response so far. And your solution also works for me, hence thought of checking with you. – user312307 Jun 19 at 15:04
  • what does numlines=3 represent? I just wanted to understand the logic how this works for a sample file and not a pom file which has more than 1000 records. – user312307 Jun 21 at 15:20
  • 1
    Okay, So I have removed the edits from this question and posted a new question here - unix.stackexchange.com/questions/526269/… – user312307 Jun 21 at 16:27
2

Store the n and n+1 lines in hold when there's a match on the n-th. Read the n+2 th and examine it for the prescribed content. Upon match, recall the hold and perform the substitution to abc.

 $ sed -ne '
     />com\.icc\.id</!bp
     N;h;n
     /\<com\.icc\.id\.version\>/{
         x;s/>com\.icc\.id<\(.*\n\)/>abc<\1/;x
    }
     x;G;:p;p
 ' inp.pom
1

If your file is small enough to fit in memory, you can do:

$ awk '{lines[NR]=$0}END{for(i in lines){ if(lines[i] ~ /com\.icc\.id/ && lines[i+2] ~ /com\.icc\.id\.version/){gsub("com\\.icc\\.id","abc",lines[i])} print lines[i]}}' file 
(groupId)com.icc.id(/groupId)
(artifactId)abc(/artifactId)
(version)${project.version}(/version)
(groupId)abc(/groupId)
(artifactId)ifd(/artifactId)
(version)${com.icc.id.version}(/version) 

Or, slightly more readbly:

$ awk '{
        lines[NR]=$0
       }
       END{
            for(i in lines){ 
                if(lines[i] ~ /com\.icc\.id/ && 
                   lines[i+2] ~ /com\.icc\.id\.version/){
                        gsub(/com\.icc\.id/,"abc",lines[i])
                } 
            print lines[i]
            }
       }' file 
(groupId)com.icc.id(/groupId)
(artifactId)abc(/artifactId)
(version)${project.version}(/version)
(groupId)abc(/groupId)
(artifactId)ifd(/artifactId)
(version)${com.icc.id.version}(/version) 

To apply this to all files with the extension .pom (I assume that's what you mean by "pom files") in the directory common and any subdirectories, you can do this (assuming you are using bash, and GNU gawk):

shopt -s globstar
tmpFile=$(mktemp)
for file in common/**/*.pom; do
    awk '{
    lines[NR]=$0
   }
   END{
        for(i in lines){ 
            if(lines[i] ~ /com\.icc\.id/ && 
               lines[i+2] ~ /com\.icc\.id\.version/){
                    gsub(/com\.icc\.id/,"abc",lines[i])
            } 
        print lines[i]
        }
   }' "$file" > "$tmpFile" && mv "$tmpFile" "$file"
   done
1

With ed:

printf 'g/com\.icc\.id\.version/-2 s/com\.icc\.id/abc/\n,wq\n' | ed -s file.pom

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.