Thursday 17 July 2008

bash commands, xml cleaning and Java Searching

A lot of you are Linux users, some Mac and *NIX and the others, I want you to be *NIX. (perhaps use cygwin for a bit).

This post is primarily about two things .. helping you in the command line and helping you develop.


I am, as most people know a Java developer (by trade) and work a lot with XML. I found two things annoyed me for some time a year or two back.

1. Finding the jar of some class I had not seen (and it may be in my m2 repository)

2. Cleaning my XML files (especially the pom.xml files) so they were neat.


I solved both of these with two seperate bash functions. A bash function acts like command on the command line, so if you have




function foo () { echo "foo and Hello World together" }



in your .bashrc, when you "source ~/.bashrc or open another shell, the command "foo" will work.

Cobling this together with some good old unix commandline foo, you can do amazing stuff (I remember the first complex function I wrote was a wrapper for df (disk filesystem) and using awk to show the percentage of usage (of about 2MB) that I had used on the UNE CS Dec Alpha Server, mihi and later neumann).

Sadly I don't have that command anymore, but I don't need it either as df -h gives you the wonderful details. I digress.

Back to functions; it is all well and good to have them, but if you don't remember them, or how to run them, they become dusty (and your self help shell foo rusty).

To solve this problem, I created my own simple help system, "myhelp" nothing robust, nothing fancy but it all works within the one .bashrc file. The principle is simple. Any function I write for me, adheres to two principles.
  1. It calls a "help" function first off, which checks if the first argument is --help and promptly displays the help and exits the function.
  2. After the first { on the same line as the "function ..." brace, i add a comment # myFunction which signifies it is special and help can be listed for it. (and that is is my function)
Now to the functions themselves.
1. First the XML Cleaner.

#
# Use XMLLint to reformat the XML
#
function xmlclean () { # myFunction
myShowHelp $1 "xmlclean" " - Reformat the XML Document" || return

doc=$1;
tmpDoc=/tmp/xcleaner.$$.xml
cp ${doc} ${tmpDoc} && xmllint --format ${tmpDoc} --output ${doc} && rm ${tmpDoc}
}

The indentation is a little skewed (I'll try and resolve that later). This bash script uses xmllint

sudo apt-get install libxml2-utils
Fairly simple, in fact I THINK that the xmllint can do an inline replacement, but hey mine works.

so .. formatting an XML file is simply ..

rbuckland@ld630:~$ xmlclean foo.xml


2. Now for locating classes in Jars ..
#
# find a java class in the Repo
#
function jfind () { # myFunction
myShowHelp $1 "jfind" " - Find a classname(substring also) in Maven2 jars. The classname is passed to egrep on the jar contents" || return
find ~/.m2/repository/ -name '*.jar' | xargs -l1 -ixx sh -c "jar tvf xx | egrep $1 && echo xx"
}

An example of it in action:
rbuckland@ld630:~$ jfind DxmlExecutor
1842 Fri Jul 04 09:09:06 EST 2008 com/mivira/dxml/core/DxmlExecutor.class
/home/rbuckland/.m2/repository/com/mivira/dxml/dxml/0.9.1a/dxml-0.9.1a.jar

I won't go into what it is doing (it's pretty simple) just run each command seperately and you will get it all.

3. Now for the help system,

To show your custom commands you have written, type the following.

rbuckland@ld630:~$ myhelp
jfind - Find a classname(substring also) in Maven2 jars. The classname is passed to egrep on the jar contents
xmlclean - Reformat the XML Document
rbuckland@ld630:~$


To see the help for one command ..

rbuckland@ld630:~$ xmlclean --help
xmlclean - Reformat the XML Document

The myhelp works simply by looking for all "function name () { #myFunction" lines in your .bashrc and runs functionName --help for each one.

here is the two help functions you need in the .bashrc.

# declare -F shows the functions, and the shopt 'extdebug' is meant to show the line numbers
# of the functions (and src file) but it didn't so I am going with a more fool proof
# way of identifying my functions .. grep the .bashrc for all function lines that have the
# comment on the same line (hack but works)
#
function myhelp () {
for i in `grep myFunction ~/.bashrc | cut -f2 -d' '`
do
$i --help
done
}

function myShowHelp() {
if [ $1 == '--help' ]; then
echo $2 $3
return 1
fi
return 0
}


That's it. Now when I add a new function. All I have to do is create it according to my template which is

function mynewcommand () { # myFunction
myShowHelp $1 "mynewcommand" "description" || return

# do all the work here
}


and that's it, re-source my .bashrc (source ~/.bashrc) and test it out. and myhelp just automagically picks it up for me.

Done!

Addendum: As is is often the case, looking at my old code I make imporevements and posting this blog entry did just that.

I figured that maven related tasks should be prefixed with m2. and also I added another helper for maven.

Here is my full set of relevant helpers.
Cheers

#-----------------------------------------------------------------------
# declare -F shows the functions, and the shopt 'extdebug' is meant to show the line numbers
# of the functions (and src file) but it didn't so I am going with a more fool proof
# way of identifying my functions .. grep the .bashrc for all function lines that have the
# comment on the same line (hack but works)
#
function myhelp () {
for i in `grep myFunction ~/.bashrc | cut -f2 -d' '`
do
$i --help
done
}

function myShowHelp() {
if [ $1 == '--help' ]; then
echo $2 $3
return 1
fi
return 0
}

#
# print out all the matching jars in the repo
#
function m2showversions () { # myFunction
myShowHelp $1 "m2showversions" " - Simply lists all the jar filenames that match. Infer from that the versions you have on disk"

(cd ~/.m2/repository && find * -name "*$1*")
}

#
# find a java class in the Repo
#
function m2find () { # myFunction
myShowHelp $1 "m2find" " - Find a classname(substring also) in Maven2 jars. The classname is passed to egrep on the jar contents" || return
find ~/.m2/repository/ -name '*.jar' | xargs -l1 -ixx sh -c "jar tvf xx | egrep $1 && echo xx"
}

#
# Use XMLLint to reformat the XML
#
function xmlclean () { # myFunction
myShowHelp $1 "xmlclean" " - Reformat the XML Document" || return

doc=$1;
tmpDoc=/tmp/xcleaner.$$.xml
cp ${doc} ${tmpDoc} && xmllint --format ${tmpDoc} --output ${doc} && rm ${tmpDoc}
}
#-----------------------------------------------------------------------


hope that helps

Current 5 booksmarks @ del.icio.us/pefdus