views:

389

answers:

4

I want a sed script that I can use for 1) finding instances, and 2) printing this string:

<bean:write name='iframesrcUrl'/> 
<bean:write name="iframesrcUrl"/>
<bean:write name="currentPage" property="title" filter="false"/>

or similar. name and property values can differ. property and filter attributes are optional. Both single quotes ' and double quotes " occur.

The sed command must be two headed: I want first to run one command to see what it finds. Then I want to run the next command to make the actual replacements. The strings should be replaced with:

${ iframesrcUrl }
${ currentPage.title }

A quick grep shows there are 68 occurences in my project: grep '<bean:write name=' **/* |wc -l

What would be the easiest way to solve this?

A: 

I'm going off of your grep regex here

1) Print what it finds

sed '/<bean:write name=/!d'

2) Replace what it finds

sed '/<bean:write name=/s/^.*$/${ iframesrcUrl }\n${ currentPage.title }/'

Looking further at your question, I see you appear to have Bash4 with globstar on (due to the **/* glob). If you want these sed scripts to run on each file recursively I would suggest:

#!/bin/bash

for file in **/*; do
    <sed one-liner here> "$file"
done

For the replacement sed script, just add -i to do an in-place edit. Note that this requires GNU sed to work. If you don't have GNU sed, you will have to redirect the output to a temp file.

SiegeX
Thanks a lot! I found out that matching both single and double quotes from the commandn line is pretty hard. But it can be solved by using two expressions and giving both to sed
Jesper Rønn-Jensen
A: 

its not exactly clear what your output might be. just a guess until you provide more clear sample inputs and output

awk '/bean:write name/{
    $0="${ iframesrcUrl }\n${ currentPage.title }"
}{print}' file
ghostdog74
Thanks for example. Awk still scares me a bit because i don't really completely understand advanced stuff like in your example above. For now, I stick with the sed version.
Jesper Rønn-Jensen
the awk script just says, if "bean:write name" is found, that line should be changed to "${ iframesrcUrl }\n${ currentPage.title }". the \n is the newline. then print the record. that's it.
ghostdog74
+1  A: 

Having learned from the other answers that partially covered my question, i ended up with the following.

(I bet it can be made shorter but it works)my try to find every occurence of constructions like

#my try to find every occurence of constructions like 
# <bean:write name='iframesrcUrl'/> 
# <bean:write name="iframesrcUrl"/>
# <bean:write name="currentPage" property="title" filter="false"/>
# 
# or similar. name and property values can differ. property and filter attributes are optional. 
# Both single quotes ' and double quotes " occur.
#
# cd jahia_virk/tomcat/webapps/ROOT/jsp/jahia/templates/virk/virk.dk


# Printing occurences:
# =====================
sed -nE \
-e '/<bean:write name="([[:alpha:]]+)"( property="([[:alpha:]]+)")( filter="false")?\/>/p' \
-e "/<bean:write name='([[:alpha:]]+)'( property='([[:alpha:]]+)')( filter='false')?\/>/p" \
-e '/<bean:write name="([[:alpha:]]+)"\/>/p' \
-e "/<bean:write name='([[:alpha:]]+)'\/>/p" \
*.jsp **/*.jsp **/*.inc  


# Replacing occurences:
# =====================
sed -E -i .bak \
-e 's/<bean:write name="([[:alpha:]]+)"( property="([[:alpha:]]+)")( filter="false")?\/>/${ \1.\3 }/g' \
-e "s/<bean:write name='([[:alpha:]]+)'( property='([[:alpha:]]+)')( filter='false')?\/>/\${ \1.\3 }/g" \
-e 's/<bean:write name="([[:alpha:]]+)"\/>/${ \1 }/g' \
-e "s/<bean:write name='([[:alpha:]]+)'\/>/\${ \1 }/g" \
*.jsp **/*.jsp **/*.inc

A few lessons learned:

  • $ is reserved from the command line, so I had to escape the $ sign in lines where the sed expression is within double-quotes
  • \w did not work for matching any word character. So I had to substitute with [[:alpha:]]
  • Substitutions in any file in any directory (*/* **/*) is a no-go for hidden system files, binary files like images, etc. I had to focus on only .jsp and .inc files for my project: *.jsp **/*.jsp **/*.inc

One more word of caution: I did this on a project to move it away from old-school struts style. If you are in a similar situation be careful to review any edits manually afterwards.

Script shortcomings: For various reasons, the following examples were not found with the script above:

<bean:write name='scriptEditor-Url'/>    
<bean:write name='currentSite' property='homePage.url'/>
<bean:write name="portlet" property="value" filter="false" />
<bean:write name='<%= "optTextUrl" + id %>'/>

#1 failed because [[:alpha:]] did not match - (and there are also some with underscores).

#2 is the same: [[:alpha:]] does not match a dot ..

#4 concatenates strings inside parameter name. I could write a script to find them , but there are only four occurences in the project. The big question is what it should be replaced with. I suspect inline java does not work. and I suspect I cannot just write ${ 'optTextUrl' + id }

Jesper Rønn-Jensen
A: 

Assuming that you have a file like

<root>
<bean:write name='iframesrcUrl'/> 
<bean:write name="iframesrcUrl"/>
<bean:write name="currentPage" property="title" filter="false"/>
<foo><bar/></foo>
</root>

you can do replacements with this sed command (using GNU sed):

 sed "s/<bean:write name=[\'\"]\?iframesrcUrl[\'\"]\?\/>/\${ iframesrcUrl }/g; \
      s/<bean:write name=[\'\"]\?currentPage[\'\"]\?.*\/>/\${ currentPage.title }/g;" \
     input.xml

which produces:

<root>
${ iframesrcUrl } 
${ iframesrcUrl }
${ currentPage.title }
<foo><bar/></foo>
</root>

Is it what you need? Or do you want to replace attributes' values? Or do you want to put your substitution text into these tags?

To find and edit all files in-place (attention! changes your files, please test without -i before use, put your file mask instead of '*.jsp'):

find . -type f -name '*.jsp' -print0 | xargs -0 sed -i "..."

UPDATE

To replace attribute values, not the lines of file themselves, I would strongly recommend using xmlstarlet instead of sed/awk. It is much more reliable and flexible. I cannot post solution exactly for your case, because xmlstarlet needs a complete (valid) file to process, but this is an idea:

Given a file:

<a>
   <b>
      <c name="foo"/>
      <c name="bar"/>
   </b>
</a>

Let say we want replace foo with SPAM and bar with EGGS. Then this command will do it (splitted lines for readability):

$ printf '<a><b><c name="foo"/><c name="bar"/></b></a>' | \
  xmlstarlet ed --update "//c[@name='foo']/@name" -v SPAM \
                --update "//c[@name='bar']/@name" -v EGGS
<?xml version="1.0"?>
<a>
  <b>
    <c name="SPAM"/>
    <c name="EGGS"/>
  </b>
</a>

I used XPath syntax to select an element to replace (in the first case it is name attribute which belongs to any c tag and is equal to foo). ed subcommand of xmlstarlet allows various transformations, replacing (updating) an element is just on of them.

In real-life examples you will need to specify also bean workspace, i.e. add something like

 -N bean=urn:...

to the list of xmlstarlet's options. You can find the correct URI in the first lines of your .jsp file (I don't have any to look at).

jetxee
Thanks a lot for your inspiring ideas. As you may have guessed I wanted to replace attribute's values. The in-place part is pretty trivial because I have everything versioned, so it's easy to track the actual changes
Jesper Rønn-Jensen
Then I would recommend using xmlstarlet. I'll post an example in few minutes.
jetxee