MCWong
Blah! MCWong
All | Baby | Blah | General | Singapore | Supportability | TechNote

20040714 Wednesday July 14, 2004

ksh scripting #4 : list elements v7 and v8

Alan offered cool advise on ksh scripting #3 with pl.sh using "IFS" and "set --".

... and I dig!

So I adapted pl.sh into a shell function. My initial concerns that IFS is not taken in local context of a function was valid afterall. IFS is global and most certainly not just another variable. It got confusing when echo $IFS doesn't show anything and I thought it's spaces. set | grep IFS shows differently. It got even more mysterious when the function was invoked again but produce different result. Only after a bunch of almighty debug printf (ok I used echo) that it became clear IFS was causing my confusion. Just to make matter more difficult for myself I tried to be fancy and do set -- $@ directly. That certainly doesn't work. The moment IFS is set, substitution for $@ no longer shows the char that's IFS but instead, space is substituted!

Here's pl7() which adapted the set -- trick that works:


#
#  pl7 - print list elements delimited by '|'
#
function pl7 {
  l=$@
  IFS='|'
  set -- $l
  for e
  do
    echo $e
  done
  unset IFS
}

And to add some flexibility, I can cater to user specified delimiters which would have been otherwise messy to do in pl version 1:


#
#  pl8 - print list elements, $1 is delimiter
#
function pl8
{
  d=$1
  shift
  l=$@
  IFS=$d
  set -- $l
  for e
  do
    echo $e
  done
  unset IFS
}


Give it a go:


$ pl7 "lp|x|71|8|Line Printer Admin|/usr/spool/lp|"
lp
x
71
8
Line Printer Admin
/usr/spool/lp
xxx


$ pl8 ":" lp:x:71:8:Line Printer Admin:/usr/spool/lp:
lp
x
71
8
Line Printer Admin
/usr/spool/lp
xxx
$ 

I guess my intention was clear, in that I'm trying to let ksh do all the work whenever I can avoid a process invocation. Otherwise, it's really much easier to just awk it.


$ head -1 /etc/passwd | nawk -F':' '{while (++i<=NF) print $i}'

 or a bit more readable

$ head -1 /etc/passwd | nawk -F':' '{for (i=1;i<=NF;i++) print $i}' 

PS: Okay why version 7 and 8? it took me that no. of iteration to figure out IFS. ;)

ksh scripting flashback: #1 #2 #3

(2004-07-14 00:00:04.0) Permalink Comments [3]

20040709 Friday July 09, 2004

ksh scripting #3: list elements

It seem unavoidable, at some point there is always some kind of list that needs spliting.


#
#  pl - print list elements delimited by '|'
#
function pl {
  l="$*|"

  while [ "$l" ]
  do
    e="${l%%\|*}"
    l="${l#*\|}"
    echo "$e"
  done
}

Here's to give it a go:


$ head -1 /etc/passwd | sed 's/:/|/g'     
root|x|0|1|Super-User|/|/bin/ksh
$ 
$ pl `head -1 /etc/passwd | sed 's/:/|/g'`
root
x
0
1
Super-User
/
/bin/ksh
$ 

Why '|' as separator? Well, when was the last time you see this '|' thing in the English language? And, when was the last time you process a bunch of shell command pipelines all concatenated together into a list to be processed? I supposed you get them more often with each pipeline on a separate line, as in a shell script (?!); which you could readily process with read, grep, awk, sed and a zillion other utilities.

Why not ':' as in /etc/passwd? I have scripts that process list of URL's, so 'http://...'! Not very convenient isn't it.

ksh scripting flashback: #1 #2

(2004-07-09 00:00:03.0) Permalink Comments [1]

20040708 Thursday July 08, 2004

ksh scripting #2: unix friendly filename

Every time I rip and encode tracks from my CD collections (yes I still buy them) to mp3, I had the need of getting a unix friendly filename from the human readable title since I use Solaris to do everything. I typically grab the titles from a free cddb out there that happens to have my album's details. But these come in various formats such as:


9. title of track nine
10) track-ten's title
11: track eleven title & nothing else...

While I need them to be unix friendly, such as:


9:title_of_track_nine
10:track-ten_s_title
11:track_eleven_title_+_nothing_else___

So I script it with the help of sed(1), and what a monster I've created!


#!/bin/ksh

cddb=$1

ttlist=`sed 's/^[   ]*//;/^$/d;/^[^0-9]/d;s/[	]*$//;s/[   ][	]*/ /g;\
/^[0-9][0-9]*[. _    :-)]/s/[. _ :-)][. _    :-)]*/:/;s/[".]//g;\
y/ '"'"'&\//__++/' $cddb`

#   remove leading spaces and tabs
#    s/^[   ]*//
#   remove blank lines
#    /^$/d
#   ignore lines not beginning with a number
#    /^[^0-9]/d
#   remove trailing spaces and tabs
#    s/[    ]*$//
#   compress 1 or more adjacent white spaces
#    s/[    ][	]*/ /g
#   replace characters delimiting track no. from the title, with a single delimiter
#    /^[0-9][0-9]*[. _	:-)]/s/[. _ :-)][. _	:-)]*/:/
#   remove characters that won't look good if transformed to "_"
#    s/[".]//g
#   transform space and special chars to shell friendly chars
#    y/ '&\//__++/

# create title[track] array elements
for tt in $ttlist
do
  let "track = ${tt%%:*}"
  title[$track]=${tt#*:}
done
    :

    :

I'm not even sure if I understand this now that I looked at it again. :(

The for loop that followed put the track title into an array indexed by the track no., nice and neat for whatever I want to do next.

ksh scripting flashback: #1

(2004-07-08 00:00:02.0) Permalink Comments [5]

20040707 Wednesday July 07, 2004

ksh scripting #1: ME=${0##*/}

True to the tradition of unix, this is as cryptic as it gets, but... elegant [GEEK!]


  #!/bin/ksh
  ME=${0##*/}

ME here gets the name of the script without the path, effectively doing a basename(1) without the burden of a fork and exec process invocation.

${0} - is command line token 0, i.e. the script filename
## - strips the largest leading string matching the pattern that follows
*/ - this pattern with ## matches everything upto and including the last '/', this is shell wildcard (patmat), not regex

For those who like filename with obvious indication of it's genre, e.g. blah.sh , do this:


  #!/bin/ksh
  ME=${0##*/}
  ME=${ME%.*}

% - strips the smallest trailing string matching the pattern that follows
.* - this pattern with % matches the last '.' (dot is not a wildcard in shell, '?" is) and upto the end of string

I always use $ME in my usage() function,


  usage () {
  cat <<__UsageEnd__
  usage: ${ME} [options] args...
    options:
      -a  ah
      -b  blah
  __UsageEnd__
  }

and any error output,


  perr () {
  echo "${ME}: ${SEVERITY}: ${MSGTXT}"
  }

Since my job does more code reading than writing, what's left is the occasional need to automate some test, so shell does it just fine. Instant and disposable (i.e. no worries about reusable values). Nevertheless, there are a few tricks here and there that I like to remember.

(2004-07-07 00:00:01.0) Permalink

20040617 Thursday June 17, 2004

blastwave rocks!

While having my daily fix of Sun blog (kind of becoming yet another addiction), I came across hoffie's happy endorsement of downloading gaim from blastwave and interestingly traced to maintainer torrey. Hey, that's Torrey! Can't miss Torrey if you read email support aliases. >-)

Since I had procrastinated too long to compile gaim 0.77 myself (got the source but not the time) and to think that 0.78 is already out, I figured, hey, why not, let's be lazy. I remember doing pkg-get some time ago when it was still BOLTpget.pkg, but can't recall what for; which means it probably failed for some reason.

Anyway, this time, it was painless and by far the best user experience I've had installing free stuff! Installing anything for that matter!

   pkg-get -i gaim

A couple of y's later for 'yes' to run script with su priv, Gaim 0.78 was up and running! That's all there is! Considering that I had an empty /opt/csw, so pkg-get were downloading obscene amount of packages that at some point I was actually worried. (Should I?)

With such easy success, it didn't take long for me to continue with:

   pkg-get -i gqview

And again, no problem whatsoever. This time was a lot faster considering most of the big dependent packages are already done.

blastwave ROCKS! Here's yet another satisfied customer. :-)

(2004-06-17 02:02:02.0) Permalink Comments [4]


archives
links
referers