Don’t try this as root

Contexts of a tiny script that you should never run:

cp $0 $$ # Make a copy of yourself
sh $$ & # Run the copy in the background
sh $0 & # Rerun a new version of yourself in the background

This will probably take over your server or at least use up all of the filesystem pointers in its filesytem. In a minute or so you would have thousands of these running and doubling each generation.

This does demonstrate an interesting version of ‘the game of life’. If the copy (cp) could be a little less reliable and the whole thing could work in a few messed up ways, this could actually evolve into some other different tiny script.

Self-reference is always fun times.

Functions to add LISP-like commands to KSH

The first function is ‘nth’ takes up to 4 parameters and displays those columns in the order specified:

echo "one two three four five" | nth 4 2 5
four two five

The next three functions (‘first’,’second’,’third’,’fourth’ respective field of output, executing a command if it is specified. Here is an example:

lspv | grep rootvg
hdisk0         00033f4a41bd5e90           rootvg          active
hdisk2         00033f4a23178559           rootvg          active

Without any parameters, ‘first’ simply strips off the other fields:

lspv | grep rootvg | first
hdisk0
hdisk2

With parameters, it simply applies them (like the lisp mapcar function) using xargs -n1 to create a bunch of sub-commands:

lspv | grep rootvg | first lsdev -Cl
hdisk0 Available  Virtual SCSI Disk Drive
hdisk2 Available  Virtual SCSI Disk Drive

The above command is literally two commands in one:

lsdev -Cl hdisk0 ; lsdev -Cl hdisk1

second and third are similar.

#!/bin/ksh

###################################
# Title         : functions.ksh
# Auithor       : John Rigler
# Date          : 10-22-2009
# Requires      : ksh and a .profile
###################################
# nth           : display fields
# first         : display/exec
# second        : display/exec
# third         : display/exec
# fourth        : display/exec
# ltrim         : discard X
# linesel       : show one line X
###################################



# This just wraps around awk and prints
# whatever fields are specified

function nth {
if [[ -n $4 ]]
        then
        awk "{print \$$1 , \$$2 , \$$3 , \$$4 }"
        exit
        fi

if [[ -n $3 ]]
        then
        awk "{print \$$1 , \$$2 , \$$3 }"
        exit
        fi

if [[ -n $2 ]]
        then
        awk "{ print \$$1 , \$$2 }"
        exit
        fi

if [[ -n $1 ]]
        then
        awk "{ print \$$1 }"
        exit
        fi

}

# These functions only display one field
# but will do an execution of anything
# specified, similar to mapcar in lisp

function first {

nth 1 | xargs -n1 $*

}

function second {

nth 2 | xargs -n1 $*

}

function third {

nth 3 | xargs -n1 $*

}

function fourth {

nth 4 | xargs -n1 $*

}


# This is a filter that drops X number of fields
# it does not work with multiple lines yet

function ltrim {

NEWLINE=""
let COUNT=0

read LINE
for WORD in $LINE
        do
        let COUNT=COUNT+1
        if [[ $COUNT -gt $1 ]]
                then
                NEWLINE="$NEWLINE $WORD"
                fi
        done

echo $NEWLINE

}

# A filter that simply selects only the line you specify

function linesel {

head -$1 | tail -1
}

Script to tell me what hosts are down in a list

#! /usr/bin/ksh
#################################################################
# Title      :  pinghosts - Returns status of all hosts
# Author     :  John Rigler
# Date       :  01-09-2009
# Requires   :  ksh
#################################################################



grep -v "^#" $1 | while read HOSTNAME # Only read uncommented lines
  do
    # Execute the ping once and wait 2 seconds
    ping -c 1 -w 2 $HOSTNAME  2>&1 1>/dev/null
    if [[ ! $? -eq 0 ]]
    then
        echo "$HOSTNAME down"
    fi
  done

Read Julian Date

#!/bin/ksh93

let JULIAN=$1

# Sets a month/day pair for comparison

if  [[ `leapyear $2` != true ]]
        then
        JULIANSTR="12.334 11.304 10.273 9.243 8.212 7.181 6.151 5.120 4.90 3.59 2.31 1.0"
        LASTDAY=365
        else
        JULIANSTR="12.335 11.305 10.274 9.244 8.213 7.182 6.152 5.121 4.91 3.60 2.31 1.0"
        LASTDAY=366
fi

# Exists if the Julian Date is invalid

if [[ $JULIAN -gt $LASTDAY ]]
        then
        exit
        fi

# Main loop, only prints on a match

for MONJUL in $JULIANSTR
        do
        echo $MONJUL | sed 's/\./\ /g' | read MON JUL
        if [[ $JULIAN -gt $JUL ]]
        then
        let DAY=JULIAN-JUL
        echo "$MON/$DAY/$2"
        exit
        fi
done

base.ksh by Heiner Steven converts number formats

This and other Heiner Steven scripts can be found at heinersteven.org

#! /usr/bin/ksh
##########################################################################
# Title      :	base.ksh - print number to different bases (KSH version)
# Author     :	Heiner Steven 
# Date       :	1995-03-07
# Requires   :	bc, ksh
# Category   :	Desktop
# SCCS-Id.   :	@(#) base.ksh	1.4 04/02/18
##########################################################################
# Description
#
# Changes
# 21.03.95 hs	fixed error occuring with 0xb as input (0.2)
# 14.05.96 hs	Rewritten for ksh. Speed increasement: 60x (1.0)
# 17.05.96 hs	Reset output base to 10 (1.1)
##########################################################################

PN=${0##*/}				# Program name
VER='1.4'

function Usage
{
    print -u2 "$PN - convert number to different bases, $VER (hs '96)
usage: $PN [number ...]

If no number is given, the numbers are read from standard input.
A number may be
    binary (base 2)		starting with 0b (i.e. 0b1100)
    octal (base 8)		starting with 0  (i.e. 014)
    hexadecimal (base 16)	starting with 0x (i.e. 0xc)
    decimal			otherwise (i.e. 12)"
    exit 1
}

function Msg
{
    typeset i
    for i
    do print -u2 "$PN: $i"
    done
}

function PrintBases
{
    typeset -u Number			# Convert to upper case (for "bc")
    integer Base
    typeset Dec Result

    # Determine base of the number
    for Number
    do
	case "$Number" in
	    (0B+([01]))				# binary
		Base=2	
		Number=${Number#0B} ;;
	    (0X+([0-9A-F]))			# hexadecimal
		Base=16
		Number=${Number#0X} ;;
	    (0+([0-7]))				# octal
		Base=8
		Number=${Number##0} ;;
	    ([1-9]*([0-9]))			# decimal
		Base=10;;
	    (*([0-9A-F])+([A-F])*([0-9A-F]))	# hexadecimal, no prefix
		Base=16;;
	    (*)
		Msg "illegal number $Number - ignored"
		continue;;
	esac

	# Convert to decimal number. Do not use ksh, because
	# there is an upper limit to the maximum integer size.
	set -e				# Terminate at errors
	print -p "ibase=$Base; $Number; ibase=A"
	read -p Dec

	# Let "bc" do the conversions. "bc" will
	# echo "GNUELPF" for the last line.
	# Assign the result of c() to a dummy to prevent
	# "bc" from printing the "result" of the function.
	print -p "d=c($Dec)"

	# Read lines from "bc", build one line
	Result=
	while read -p && [[ $REPLY != GNUELPF ]]
	do
	    Result="${Result:+$Result	}$REPLY"
	done
	set +e
	print "$Result"
    done
}

if (( $# > 0 ))
then
    while getopts :h Opt
    do
	case "$1" in
	    (h)	Usage;;
	    (*)	Usage;;
	esac
    done
    shift OPTIND-1
fi

bc |&					# Start "bc" as co-process
# DEBUG:
# tee in | bc | tee out |&

# Define a "bc" function for base conversion, because "bc" would
# run out of "string space" for large input otherwise.
# The "GNUELPF" string tells PrintBases() that the end of
# the calculations is reached.
set -e					# Terminate at errors
print -p '
/* Convert x to some other bases */
define c (x) {
obase=16; "hex="; x
obase=10; "dec="; x
obase=8;  "oct="; x
obase=2;  "bin="; x
obase=A
"GNUELPF
"
}
'
set +e

if (( $# > 0 ))
then
    PrintBases "$@"
else					# read from stdin
    while read Line
    do
	PrintBases $Line
    done

90% of on the fly scripting, three basic loops

One of the cool things about a robust shell such as ksh is that you can build command on the fly by adding a command one piece at a time and running it to check how you are doing. At the end, you just add an execution command and you are done. This is called REPL style coding and is one of the big advantages of shell versus something like perl, which is often associated with System Administration.

In this example, I want to remove all of the special EMC disks from my system that aren’t in a volume group. I know that is OS specific stuff, but really I am just processing a list. Here is my criteria:

  1. hdisk4 none None (exclude, this is not a power device)
  2. hdiskpower9 00c7286c92245797 oraclevg active (exclude, in use)
  3. hdiskpower12 00c7286c7f7e4c28 None (include, not in use)

So I start with a simple list command:

lspv
hdisk0 00c7286c0997490f rootvg active
hdisk1 00c9738ea12e9e1b rootvg active
hdisk4 none None

hdisk17 none None
hdisk24 none None
hdiskpower3 00c7286c1b6cd3f2 None
hdisk25 none None
hdisk26 none None

hdisk71 none None
hdiskpower11 00c7286c922353ea oraclevg active
hdiskpower15 00c7286c9222cc96 oraclevg active

Now, because I use the vi control set on my shell, I type [ESC-k], but if you were using a different
shell or command structure, you might just hit up arrow. This calls back your old command. In this shell, I then hit [shift-a] to go to end of line and enter edit mode, where I add a pipe and grep:

# lspv | grep power
hdiskpower3 00c7286c1b6cd3f2 None
hdiskpower1 00c7286c6afbe0f7 None
hdiskpower5 00c7286c11a34c03 None
hdiskpower6 none None
hdiskpower7 none None
hdiskpower8 00c7286cb2779a47 satavg active
hdiskpower0 none None
hdiskpower2 none None
hdiskpower4 none None
hdiskpower12 00c7286c7f7e4c28 None
hdiskpower13 00c7286c7f7ee6e1 None
hdiskpower14 00c7286c804ffa93 None
hdiskpower9 00c7286c92245797 oraclevg active
hdiskpower10 00c7286c9223d7f3 oraclevg active
hdiskpower11 00c7286c922353ea oraclevg active
hdiskpower15 00c7286c9222cc96 oraclevg active
hdiskpower18 00c7286cd3c4a22d None

Now, I have a much more manageable list, I repeat this to now only find disks not in a volume group. There is probably a more precise command that piping two greps into each other, but remember this is a throw away script that you will be typing in very fast while visually inspecting your data along the way:

# lspv | grep power | grep None
hdiskpower3 00c7286c1b6cd3f2 None
hdiskpower1 00c7286c6afbe0f7 None
hdiskpower5 00c7286c11a34c03 None
hdiskpower6 none None
hdiskpower7 none None
hdiskpower0 none None
hdiskpower2 none None
hdiskpower4 none None
hdiskpower12 00c7286c7f7e4c28 None
hdiskpower13 00c7286c7f7ee6e1 None
hdiskpower14 00c7286c804ffa93 None
hdiskpower18 00c7286cd3c4a22d None

Now for the loop, you can go two ways with this, a while loop or ‘xargs’. I never can remember all of
the flags for xargs, so I usually only use it if I can without flags, but what I need is to ignore fields two and three at this point (even though we used field three as a grep criteria. Anyhow, here is the while loop. As before, bring back your old command and go to the end of the line:

# lspv | grep power | grep None | while read DISK TRASH
>

Even though you have hit enter, the while loop isn’t logically done. What we have done so far is
start the loop and read the first field into the variable DISK and the other stuff into TRASH. This is
a handy way to say, just give me the first field. If you have more fields than variables, they all end up in the last. If you need the second field, just do this instead ( while read TRASH PVID TRASH ).

# lspv | grep power | grep None | while read DISK TRASH
> do
>

Logic still not done until we get to ‘done’, you can now add as many lines as you want to act on
$DISK.

# lspv | grep power | grep None | while read DISK TRASH
> do
> echo $DISK
> rmdev -dl $DISK
> done

hdiskpower3
hdiskpower3 deleted
hdiskpower1
hdiskpower1 deleted
hdiskpower5
hdiskpower5 deleted
hdiskpower6
hdiskpower6 deleted
hdiskpower7
hdiskpower7 deleted
hdiskpower0
hdiskpower0 deleted
hdiskpower2
hdiskpower2 deleted
hdiskpower4
hdiskpower4 deleted
hdiskpower12
hdiskpower12 deleted
hdiskpower13
hdiskpower13 deleted
hdiskpower14
hdiskpower14 deleted
hdiskpower18
hdiskpower18 deleted

If you think the while loop and second shell is a little cumbersome, then introduce awk and xargs:

# lspv | grep power | grep None | awk ‘{ print $1 }’ | xargs -n1 rmdev -dl

Here is a third way to accomplish this if you are a big fan of back-tick:

for DISK in `lspv | grep power | grep None | awk ‘{print $1}’`
do
rmdev-dl $DISK
done

This command is a little more awkward because if you crafted it in a REPL fashion while checking your logic and data, you would probably start with:

lspv | grep power | grep None | awk ‘{print $1}’

Then you would have to go to both the beginning and end of the line, to wrap this is a backquote.

Here are some other ideas for isolating only the first field of data:

lspv | grep power | grep None | sed ‘s/ .*//g’
lspv | grep power | grep None | cut -c 0-12

Ultimately, I almost always pick the while loop though, because it is just so fast and you can load or discard all sorts of variables on the fly. Just put an echo before your command if you aren’t sure of how it will look and bring the whole thing back and take it out when you are ready to run. Here is a case when a for loop is cool:

# for X in hdiskpower6 hdiskpower7 hdiskpower8
> do
> extendvg oraclevg1 $X
> done