· simerian · UNIX Process List Tree

#!/usr/bin/ksh
#
# COPYRIGHT (c) 2003 - SIMERIAN
# 
# e: info@simerian.com
# w: www.simerian.com
#
# DISCLAIMER
# The author of this product does not accept any responsibility for
# loss or damages resulting from the use of said product and makes no
# warranty or representation, either express or implied, including but
# not limited to, any implied warranty of merchantability or fitness for a
# particular purpose. This product is provided "AS IS", and you, its user,
# assume all risks when using it.
#
# DISTRIBUTION
# You may freely redistribute this product subject to the following conditions:
# 1) that the whole product is redistributed, AND,
# 2) that the product or any of its components are NOT altered, AND,
# 3) that no charge be made for any redistribution (ex. consumables & handling).

#---v----1----v----2----v----3----v----4----v----5----v----6----v----7----v----

#--------------------------------------------------------------------------
ps_Tree () {

	awk '

BEGIN {

	ROOT="......"

	INDENT_SPC="                    " # 20 spaces for embedding depth **
	INDENT_BIT="00000000000000000000" # 20 spaces for embedding depth

	c_addr	=01 ; clnColIDX_Key[c_addr]	="ADDR"
	c_cls	=02 ; clnColIDX_Key[c_cls]	="CLS"
	c_cmd	=03 ; clnColIDX_Key[c_cmd]	="CMD"
	c_comd	=04 ; clnColIDX_Key[c_comd]	="COMD"
	c_command	=05 ; clnColIDX_Key[c_command]	="COMMAND"
	c_c		=06 ; clnColIDX_Key[c_c]		="C"
	c_elapsed	=07 ; clnColIDX_Key[c_elapsed]	="ELAPSED"
	c_f		=08 ; clnColIDX_Key[c_f]		="F"
	c_gid	=09 ; clnColIDX_Key[c_gid]	="GID"
	c_group	=10 ; clnColIDX_Key[c_group]	="GROUP"
	c_cpu	=11 ; clnColIDX_Key[c_cpu]	="%CPU"
	c_ni		=12 ; clnColIDX_Key[c_ni]		="NI"
	c_pgid	=13 ; clnColIDX_Key[c_pgid]	="PGID"
	c_pid	=14 ; clnColIDX_Key[c_pid]	="PID"
	c_ppid	=15 ; clnColIDX_Key[c_ppid]	="PPID"
	c_pri	=16 ; clnColIDX_Key[c_pri]	="PRI"
	c_prmid	=17 ; clnColIDX_Key[c_prmid]	="PRMID"
	c_prmgrp	=18 ; clnColIDX_Key[c_prmgrp]	="PRMGRP"
	c_rgid	=19 ; clnColIDX_Key[c_rgid]	="RGID"
	c_rgroup	=20 ; clnColIDX_Key[c_rgroup]	="RGROUP"
	c_ruid	=21 ; clnColIDX_Key[c_ruid]	="RUID"
	c_ruser	=22 ; clnColIDX_Key[c_ruser]	="RUSER"
	c_sid	=23 ; clnColIDX_Key[c_sid]	="SID"
	c_s		=24 ; clnColIDX_Key[c_s]		="S"
	c_stime	=25 ; clnColIDX_Key[c_stime]	="STIME"
	c_sz		=26 ; clnColIDX_Key[c_sz]		="SZ"
	c_time	=27 ; clnColIDX_Key[c_time]	="TIME"
	c_tt		=28 ; clnColIDX_Key[c_tty]	="TT"
	c_tty	=29 ; clnColIDX_Key[c_tty]	="TTY"
	c_uid	=30 ; clnColIDX_Key[c_uid]	="UID"
	c_user	=31 ; clnColIDX_Key[c_user]	="USER"
	c_vsz	=32 ; clnColIDX_Key[c_vsz]	="VSZ"
	c_wchan	=33 ; clnColIDX_Key[c_wchan]	="WCHAN"

	for (KeyIDX in clnColIDX_Key) {
		NameIDX=clnColIDX_Key[KeyIDX]
		clnColIDX_Name[NameIDX]=KeyIDX
	}

	c_Width=1

	clnColObj[c_addr,c_Width]		="#|%8s"
	clnColObj[c_cls,c_Width]		="#|%s"
	clnColObj[c_cmd,c_Width]		="e|%s"
	clnColObj[c_comd,c_Width]		="e|%s"
	clnColObj[c_command,c_Width]	="e|%s"
	clnColObj[c_c,c_Width]			="#|%s"
	clnColObj[c_elapsed,c_Width]	="#|%s"
	clnColObj[c_f,c_Width]			="#|%4s"
	clnColObj[c_gid,c_Width]		="#|%s"
	clnColObj[c_group,c_Width]		="#|%s"
	clnColObj[c_cpu,c_Width]		="#|%s"
	clnColObj[c_ni,c_Width]		="#|%2s"
	clnColObj[c_pgid,c_Width]		="#|%s"
	clnColObj[c_pid,c_Width]		="#|%5s"
	clnColObj[c_ppid,c_Width]		="#|%5s"
	clnColObj[c_pri,c_Width]		="#|%3s"
	clnColObj[c_prmid,c_Width]		="#|%s"
	clnColObj[c_prmgrp,c_Width]		="#|%s"
	clnColObj[c_rgid,c_Width]		="#|%s"
	clnColObj[c_rgroup,c_Width]		="#|%s"
	clnColObj[c_ruid,c_Width]		="#|%s"
	clnColObj[c_ruser,c_Width]		="#|%s"
	clnColObj[c_sid,c_Width]		="#|%s"
	clnColObj[c_s,c_Width]			="#|%1s"
	clnColObj[c_stime,c_Width]		="8|%8s"
	clnColObj[c_sz,c_Width]		="#|%5s"
	clnColObj[c_time,c_Width]		="#|%7s"
	clnColObj[c_tt,c_Width]		="#|%s"
	clnColObj[c_tty,c_Width]		="#|%-8s"
	clnColObj[c_uid,c_Width]		="#|%8s"
	clnColObj[c_user,c_Width]		="#|%s"
	clnColObj[c_vsz,c_Width]		="#|%s"
	clnColObj[c_wchan,c_Width]		="#|%16s"

	fDepth=0
}

function SortPIDAscending (p_IDX_Sort,p_SortMax) {

	fComplete=0
	while (fComplete == 0) {
		fSwapped=0
		for (SortNo=1; SortNo < p_SortMax; SortNo++) {
			if (p_IDX_Sort[SortNo] > p_IDX_Sort[(SortNo+1)]) {
				fSwapped=1
				TMP=p_IDX_Sort[SortNo]
				p_IDX_Sort[SortNo]=p_IDX_Sort[(SortNo+1)]
				p_IDX_Sort[(SortNo+1)]=TMP
			}
		}
		if (fSwapped == 0) fComplete=1
	}
}

function TraceTree (p_ChildKey,p_SRCMax,p_IDX_SRC,p_IDX_TGT,p_SRCNo) {

	SRCNo=1

	Prefix=substr(INDENT_SPC,1,((EmbeddingDepth+0)*2))

	while (SRCNo <= p_SRCMax) {
		if (p_IDX_SRC[SRCNo] != "") {
			if ((EmbeddingDepth+0) == 0) {
				p_ChildKey=substr(p_IDX_SRC[SRCNo],1,6)
			}
			ParentKey=substr(p_IDX_SRC[SRCNo],1,6)
			if (ParentKey == p_ChildKey) {
				++TGTNo
				p_IDX_TGT[TGTNo]=p_IDX_SRC[SRCNo]
				p_IDX_SRC[SRCNo]=""
				ChildKey=substr(p_IDX_TGT[TGTNo],7,6)
				PIDVal=ChildKey+0
				LineNo=IDX_pid[PIDVal]
				IDX_stdin[LineNo,fDepth]=EmbeddingDepth+0
				EmbeddingDepth++
				SRCNo=TraceTree(ChildKey,p_SRCMax,p_IDX_SRC,p_IDX_TGT,SRCNo)
				EmbeddingDepth--
				Prefix=substr(INDENT_SPC,1,((EmbeddingDepth+0)*2))
			}
		}
		SRCNo++
	}

	return p_SRCNo
}

NR == 1 {

	sub("^[ ]*","",$0)
	sub("[ ]*$","",$0)
	gsub("[ ]+"," ",$0)

	FldMax=split($0,cln," ")

	fIndent=FldMax+1

	for (FldNo=1; FldNo <= FldMax; FldNo++) {
		for (NameIDX in clnColIDX_Name) {
			if (cln[FldNo] == NameIDX) {
				KeyIDX=clnColIDX_Name[NameIDX]
				clnColObj[KeyIDX,c_Posn]=FldNo
				clnFldObj[FldNo]=KeyIDX
			}
		}
	}

	fPID=0
	fPPID=0
	for (FldNo=1; FldNo <= FldMax; FldNo++) {
		KeyIDX=clnFldObj[FldNo]
		ColName=clnColIDX_Key[KeyIDX]

		if (ColName == clnColIDX_Key[c_pid]) fPID=FldNo
		if (ColName == clnColIDX_Key[c_ppid]) fPPID=FldNo

		if (ColName == clnColIDX_Key[c_cmd]) fCMD=FldNo
		if (ColName == clnColIDX_Key[c_comd]) fCMD=FldNo
		if (ColName == clnColIDX_Key[c_command]) fCMD=FldNo
	}
}

NR > 1 {

	ChrPosn=1
	for (FldNo=1; FldNo <= FldMax; FldNo++) {
		KeyIDX=clnFldObj[FldNo]
		ColWidth=clnColObj[KeyIDX,c_Width]
		split(ColWidth,clnWidth,"|")
		ColSize=clnWidth[1]
		ColFormat=clnWidth[2]
		FldData=""
		if (ColSize == "#") {
			SubString=substr($0,ChrPosn)
			match(SubString,"^[ ]*[[:graph:]]+[ ]+")
			FldData=substr(SubString,1,RLENGTH)
			ColSize=RLENGTH
		} else {
			if (ColSize == "e") {
				FldData=substr($0,ChrPosn)
				ColSize=length($0)-ChrPosn
			} else {
				FldData=substr($0,ChrPosn,ColSize)
			}
		}
		sub("^[ ]*","",FldData)
		sub("[ ]*$","",FldData)
		ChrPosn+=ColSize

		IDX_stdin[NR-1,FldNo]=FldData
	}
	if (fPID) {
		PIDVal=IDX_stdin[NR-1,fPID]
		IDX_pid[PIDVal]=NR-1
		SortKey=sprintf("%06d",PIDVal)
		if (fPPID) {
			PPIDVal=IDX_stdin[NR-1,fPPID]
			IDX_ppid[PPIDVal]=NR-1
			SortKey=sprintf("%06d%s",PPIDVal,SortKey)
		}
		IDX_SORT[NR-1]=SortKey
	}
}

END {

	LineMax=NR-1

	OverflowMax=LineMax
	if (fPID && fPPID) {
		for (PPIDVal in IDX_ppid) {
			if (IDX_pid[PPIDVal] == "") {
				OverflowMax++
				for (FldNo=1; FldNo <= FldMax; FldNo++) {
					IDX_stdin[OverflowMax,FldNo]="-"
				}
				IDX_stdin[OverflowMax,fPID]=PPIDVal
				IDX_stdin[OverflowMax,fPPID]="?"
				IDX_stdin[OverflowMax,fCMD]="[parent]"
				IDX_pid[PPIDVal]=OverflowMax
				SortKey=sprintf("%6.6s%06d",ROOT,PPIDVal)
				IDX_SORT[OverflowMax]=SortKey
			}
		}
	}

	if (fPID) SortPIDAscending(IDX_SORT,OverflowMax)

	if (fPID) {
		if (fPPID) {
			TraceTree("",OverflowMax,IDX_SORT,IDX_TREE,1)
			for (LineNo=1; LineNo <= OverflowMax; LineNo++) {
				PIDVal=substr(IDX_TREE[LineNo],7,6)+0
				stdinNo=IDX_pid[PIDVal]
				for (FldNo=0; FldNo <= FldMax; FldNo++) {
					IDX_stdout[LineNo,FldNo]=IDX_stdin[stdinNo,FldNo]
				}
			}
		} else {
			for (LineNo=1; LineNo <= OverflowMax; LineNo++) {
				PIDVal=IDX_SORT[LineNo]+0
				stdinNo=IDX_pid[PIDVal]
				for (FldNo=1; FldNo <= FldMax; FldNo++) {
					IDX_stdout[LineNo,FldNo]=IDX_stdin[stdinNo,FldNo]
				}
			}
		}
	} else {
		for (LineNo=1; LineNo <= OverflowMax; LineNo++) {
			for (FldNo=1; FldNo <= FldMax; FldNo++) {
				IDX_stdout[LineNo,FldNo]=IDX_stdin[LineNo,FldNo]
			}
		}
	}

	DepthPrev=0
	for (LineNo=OverflowMax; LineNo >= 1; LineNo--) {
		DepthVal=IDX_stdout[LineNo,fDepth]
		if (DepthVal > 0) {
			if (DepthVal < DepthPrev) {
				Prefix=substr(INDENT_BIT,1,DepthVal-1)
				Suffix=substr(INDENT_BIT,DepthVal+1)
				gsub("1","0",Suffix)
				INDENT_BIT=sprintf("%s1%s",Prefix,Suffix)
			} else {
				if (DepthVal > DepthPrev) {
					Prefix=substr(INDENT_BIT,1,DepthVal-1)
					Suffix=substr(INDENT_BIT,DepthVal+1)
					INDENT_BIT=sprintf("%s1%s",Prefix,Suffix)
				}
			}
		} else {
			gsub("1","0",INDENT_BIT)
		}
		DepthPrev=DepthVal
		IDX_stdout[LineNo,fIndent]=sprintf("%s",INDENT_BIT)
	}

	for (FldNo=1; FldNo <= FldMax; FldNo++) {
		KeyIDX=clnFldObj[FldNo]
		ColWidth=clnColObj[KeyIDX,c_Width]
		split(ColWidth,clnWidth,"|")
		ColSize=clnWidth[1]
		ColFormat=clnWidth[2]
		printf ColFormat,clnColIDX_Key[KeyIDX]
		printf " "
	}
	printf "\n"

	for (LineNo=1; LineNo <= OverflowMax; LineNo++) {
		INDENT_BIT=IDX_stdout[LineNo,fIndent]
		sub("^0*$","",INDENT_BIT)
		sub("1[0]*$","|_",INDENT_BIT)
		gsub("1","| ",INDENT_BIT) # Replace with Pipe & 1 space
		gsub("0","  ",INDENT_BIT) # Replace with 2 spaces
		IDX_stdout[LineNo,fIndent]=sprintf("%s",INDENT_BIT)

		for (FldNo=1; FldNo <= FldMax; FldNo++) {
			if (FldNo == fCMD) {
				printf "%s",IDX_stdout[LineNo,fIndent]
			}

			KeyIDX=clnFldObj[FldNo]
			ColWidth=clnColObj[KeyIDX,c_Width]
			split(ColWidth,clnWidth,"|")
			ColSize=clnWidth[1]
			ColFormat=clnWidth[2]
			printf ColFormat,IDX_stdout[LineNo,FldNo]
			printf " "
		}
		printf "\n"
	}
}

	' <&0

}
#--------------------------------------------------------------------------
ps ${*} | ps_Tree

� Apologies for the format, the tabs were converted!
� Attempted to upload file, site reported that 16k file was bigger than 1MB - Go figure!
� File can be e-mailed by return to unix@simerian.com
� Feedback appreciated.

As the more adept amongst you will see, the script accepts the standards (HP-UX) command line options, and, conditional on the PID and PPID being requested then a genealogical tree of the processes will be produced.

Some UNIX implementations have this as standard, but many do not and it is useful when monitoring and debugging troublesome process hierarchies.

Thanks

if you use the \[code\] \[/code\] tags it should preserver your formating.

Thanks Optimus_P