LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 03-25-2005, 06:29 PM   #1
lowpro2k3
Member
 
Registered: Oct 2003
Location: Canada
Distribution: Slackware
Posts: 340

Rep: Reputation: 30
Another bash question (I'm on a role :p)


Now I'm trying to write a mini-script that uses a -p option with a ProcessID as an argument. Example:
Code:
[prompt]$ ./findpid -p 123456
Process not found!
[prompt]$ echo $?
1
[prompt]$ ./findpid -p 1111
root     1111  0.0  0.0  1392 372 tty6     S   Mar17  0:00    /sbin/foo
[prompt]$ echo $?
0
I've got a script that does that, and it works. The only thing is, its not handling regular expressions properly. The regexp in the grep command I'm using is saying:

"from the beginning of the line, match any number of upper or lowercase characters, followed by any number of spaces, and return the number after those spaces, followed by a space"

See the output of me running the script above or below if that didnt make sense. I'm trying to capture the 2nd field of a ps aux command, the Process ID. The script needs to read in regular expressions as well as hard-coded PID's. So far only the hard-coded values work properly in the script, passing regular expressions, even simple ones causes unexpected output, and I can't see why:

Code:
#!/bin/bash2

declare PID_Input
declare Command_Return_Value

while getopts p: arguments 2>/dev/null
do
  case $arguments in
    p) PID_Input=$OPTARG;;
   \?) echo "Usage: pidinput [ -p PID ]"; exit 1;;
  esac
done

# See if the $PID_Input matches a process ID from a ps aux command
ps aux | grep -q "^[A-Za-z].* .*$PID_Input "
CommandReturnValue=$?

if [ "$CommandReturnValue" == "1" ]; then  # if grep did not find PID
    echo "Process not found!"
    exit 1
fi

# Still here? Ok, re-run ps aux | grep without the -q (quiet) flag,
# we want to see the output this time.
ps aux | grep "^[A-Za-z].* .*$PID_Input "
I want to be able to handle regular expressions as input, like so:

Code:
[prompt]$ ./findpid -p 1..1
root     1011  0.0  0.0  88   372 ?        S   Mar17  0:00    /sbin/foo
root     1101  0.5  0.3  1511 372 ?        S   Mar17  0:00    /sbin/lowpro2k3
root     1111  0.0  0.0  1392 372 tty6     S   Mar17  0:00    /sbin/foo
user     1411  0.2  2.6  1112 372 ?        S   Mar17  0:00    /home/barbin
mysql    1551  0.1  0.4  11392 72 tty3     S   Mar17  0:00    /usr/local/foo
root     1691  0.4  0.1  420  372 ?        S   Mar17  0:00    /sbin/foo
[prompt]$ echo $?
0
[prompt]$ ./findpid -p [1-4]
root        1  0.0  0.0  1416   456 ?        S   Mar17  0:04  init
root        2  0.0  0.0     0   456 ?        S   Mar17  0:00  [keventd]
root        3  0.0  0.0     0   456 ?        S   Mar17  0:00  [kmapd]
root        4  0.0  0.0     0   456 ?        S   Mar17  0:00  [ksoftirqd_CPU0]

[prompt]$ echo $?
0
[prompt]$ ./findpid -p 1234[5-6][6-7]78[0-3]0
Process not found!
[prompt]$ echo $?
0
 
Old 03-25-2005, 08:53 PM   #2
lowpro2k3
Member
 
Registered: Oct 2003
Location: Canada
Distribution: Slackware
Posts: 340

Original Poster
Rep: Reputation: 30
I found a fix for it. It seems like some people looked at this question, so I might as well post what the problem was in case anyone searches for a bash question in the future.

I don't fully understand the logic here, but the regular expression I used was slightly off. I wrapped the open word \< and close word \> brackets around the input variable to make sure it was a word being searched. When I was using regular expressions such as dot or question mark operators, I was sometimes picking up spaces following the process ID. Heres the old expression that didnt work:
Code:
ps aux | grep "^[A-Za-z].* .*$PID_Input "
and here's the correct one (notice open word/close word surrounding $PID_Input
Code:
ps aux | grep "^[A-Za-z].* .*\<$PID_Input\> "
and the fixed script
Code:
#!/bin/bash2

declare PID_Input
declare Command_Return_Value

while getopts p: arguments 2>/dev/null
do
  case $arguments in
    p) PID_Input=$OPTARG;;
   \?) echo "Usage: pidinput [ -p PID ]"; exit 1;;
  esac
done

# See if the $PID_Input matches a process ID from a ps aux command
ps aux | grep -q "^[A-Za-z].* .*\<$PID_Input\> "
CommandReturnValue=$?

if [ "$CommandReturnValue" == "1" ]; then  # if grep did not find PID
    echo "Process not found!"
    exit 1
fi

# Still here? Ok, re-run ps aux | grep without the -q (quiet) flag,
# we want to see the output this time.
ps aux | grep "^[A-Za-z].* .*\<$PID_Input\> "
 
Old 03-28-2005, 03:39 PM   #3
mrGenixus
Member
 
Registered: Dec 2004
Location: Colorado, US
Distribution: gentoo, debian, ubuntu live gnome 2.10
Posts: 440

Rep: Reputation: 30
care to attach complete script file with name and info so other people can use it. Also: where did you find your information
 
Old 03-28-2005, 04:02 PM   #4
TheLinuxDuck
Member
 
Registered: Sep 2002
Location: Tulsa, OK
Distribution: Slack, baby!
Posts: 349

Rep: Reputation: 33
lowpro:

Another easier way to obtain columned information from output is by using a pipe to awk:
Code:
 ps auwx | awk '{print $2;}'
That will print nothing but a listing of process ID's. $1, $2, $3, etc indicate the columns.
 
Old 03-28-2005, 07:45 PM   #5
lowpro2k3
Member
 
Registered: Oct 2003
Location: Canada
Distribution: Slackware
Posts: 340

Original Poster
Rep: Reputation: 30
I'm just starting to learn Awk right now (well yesterday/2 days ago). I have another assignment due tonite in Awk, and I'm having a bit of difficulty with it (came here to post a question, check it out if its in the next 3 hours).

ps aux | awk '{ print $2 }'

is alot easier than

ps aux | grep "^[A-Za-z].* .*$PID_Input "



I know that now I think our teacher wants us to do things the hard way before the easy way, so we appreciate the easy way
 
Old 03-28-2005, 07:52 PM   #6
lowpro2k3
Member
 
Registered: Oct 2003
Location: Canada
Distribution: Slackware
Posts: 340

Original Poster
Rep: Reputation: 30
Quote:
Originally posted by mrGenixus
care to attach complete script file with name and info so other people can use it. Also: where did you find your information
I'm sure theres better ways, but for my first 'real' bash/sh script I think its pretty darn OK

bam


ps+
Code:
#!/bin/bash

declare -i cmdResult=0

function psController()
{
    # First thing done is a check to see if there's anything to
    # output. Running grep with a -q flag runs it in 'quiet'
    # mode. I do this, searching if the $USER_INPUT matches
    # a username. After the command, the return value of grep
    # is set in the cmdResult variable. If the user doesn't exist
    # or have any processes running, a msg is displayed and
    # the script exits
    ps aux | grep -q "^\<$USER_INPUT\> "
    cmdResult=$?

    if [ "$cmdResult" == "1" ] && [ "$USER_INPUT" != "" ]
    then
        echo "User not found!"
        exit 1
    fi

    # Here I basically do the same thing as above, but this time I
    # do a quiet grep search to see if the Process ID exists. If it
    # doesn't, an error message is displayed, and the script exits
    ps aux | grep -q "^[A-Za-z0-9].* .*\<$PID_INPUT\> .*[0-9].*\.[0-9].* "
    cmdResult=$?

    if [ "$cmdResult" == "1" ] && [ "$PID_INPUT" != "" ]
    then
        echo "Process not found!"
        exit 1
    fi


    # Display the formatted table row, before calling any functions to
    # output data.
    displayTableHeader

    # If the -u flag and an argument were passed to the script,
    # and there is users that match the argument, call the
    # showUserProcesses function
    if [ "$USER_INPUT" != "" ]
    then
        showUserProcesses
    fi

    # This does the same as above, but calls the Process ID
    # function instead
    if [ "$PID_INPUT" != "" ]
    then
        showProcessID
    fi

    # If the -c flag was set when running the script, call the
    # function which displays processes with non-zero CPU usage
    if [ "$NONZERO_FLAG" = "1" ]
    then
        showNonZeroCPU
    fi
}

function showUserProcesses()
{
    # match -u argument from ps aux output with a regular expression
    ps aux | grep "^\<$USER_INPUT\> "
}

function showProcessID()
{
    # match -p argument from ps aux output with a regular expression
    ps aux | grep "^[A-Za-z0-9].* .*\<$PID_INPUT\> .*[0-9].*\.[0-9].* "
}

function showNonZeroCPU()
{
    # show processes with a non-zero CPU usage
    ps aux | grep -v "^[A-Za-z0-9].* .*[0-9].* .*\<0.0\>" | grep -v "^USER .*"
}

function displayTableHeader
{       
    	printf "%-11s%-4s%-5s%-7s%-5s%-4s%-9s%-5s%-8s%-5s%-25s\n" \
        "USER" "PID" "%CPU" "%MEM" "VSZ" "RSS" \
        "TTY" "STAT" "START" "TIME" "COMMAND"
}

function exitError()
{
    # Display usage when someone enters wrong command-line options
    echo "Usage: ps+ [ -u username ] [ -p PID ] [ -c ]"
    exit 1
}


declare USER_INPUT=""   # User string to search for - cmd-line argument of -u
declare PID_INPUT=""    # Process ID to search for  - cmd-line argument of -p
declare NONZERO_FLAG=0  # Set to 1 if -c flag is set at the command-line


# This while loop parses the command-line arguments, using the
# getopts function. Both -u and -p must be followed by arguments,
# and the -c flag should be sent to the script without arguments.
# Error messages from getopts are redirected to /dev/null
while getopts u:p:c arguments 2>/dev/null
do
  # Start case selection statement for command-line arguments
  case $arguments in

    # The user entered -u as an argument, we must search by user_id.
    # This case statement makes sure that users don't enter -u more
    # than once, if -u is entered more than once control is sent
    # to an error function and the script exits with return value of 1

    # Make sure that -u flag is only passed once
    u) if [ "$USER_INPUT" = "" ]
       then
          # Set USER_INPUT to the argument of -u
          USER_INPUT=$OPTARG
       else
           # Exit script if -u was entered twice
           exitError
       fi;;

    # Make sure that -p flag is only passed once
    p) if [ "$PID_INPUT" = "" ]
       then
          # Set PID_INPUT to the argument of -p
          PID_INPUT=$OPTARG
       else
          # Exit script if -p was entered twice
          exitError
       fi;;

    # Set NONZERO_FLAG to 1 if the -c option was
    # given at the command line
    c) NONZERO_FLAG=1;;

    # Exit script if any other options were passed
   \?) exitError;;
  esac
done

# If no command line options were passed to the script, exit quietly
if [ "$USER_INPUT" = "" ] && 
   [ "$PID_INPUT" = ""  ] && 
   [ $NONZERO_FLAG = 0  ]
then
    exit 1
fi

# Call function psController to take control of output
psController
It used to be in 2 files, (ps+ and ps+fns) and I used to source ps+fns into ps+. I just copied/pasted all the functions at the top, if you want to see how it works, skip all the functions first and jump right to the "while getopts..." line, thats basically where I start my 'main() function'.

Other than that, hopefully someone can get some sort of use from this, I sure spent a long time on it
 
Old 03-28-2005, 07:55 PM   #7
lowpro2k3
Member
 
Registered: Oct 2003
Location: Canada
Distribution: Slackware
Posts: 340

Original Poster
Rep: Reputation: 30
Quote:
Originally posted by mrGenixus
Also: where did you find your information
This book

I saw it at the beginning of the semester and scoffed at it, thinking that one book couldnt possibly cover grep, sed, awk, bash, tcsh, korn, bourne, and lots more, but its become my #1 most used book over the past few weeks. It might be more helpful to me because my curriculum if fairly built around this book, but I seriously say that its a pretty good book. The bash chapters dont assume you read the previous korn shell chapters, and theres a CD with textfiles to manipulate (their small, and also printed in the book if your lazy like me and dont want to hunt the CD for one file )

I would recommend it to someone who wants to learn a bit more about regexp's and alot more about shell programming.
 
Old 03-28-2005, 10:12 PM   #8
lowpro2k3
Member
 
Registered: Oct 2003
Location: Canada
Distribution: Slackware
Posts: 340

Original Poster
Rep: Reputation: 30
I'll post my "man page" (written in MS word for assignment submission )

I forgot my original question was for a simpler 'piece' of my script. The script I posted above is the complete version, you can perform the following operations:

* search for processes owned by a particular user
* search for a process by a particular process ID
* display processes with non-zero CPU usage

You use command-line flags to achieve these results

-u username
-p PID
-c


If anyones interested in using it (dont see why you would, but who knows), and assuming your using windows, its probably easiest to copy what I posted, paste it into notepad/wordpad/text editor, save it as ps+ (no file extension) and ftp/scp it to your linux computer.

Then when you want to use it, heres a few examples of how:

Code:
[prompt]$ ./ps+
Nothing displays
[prompt]$ ./ps+ -u root
Displays all processes owned by root
[prompt]$ ./ps+ -p 1234
Displays process with PID of 1234 or "Process not found!" message
[prompt]$ ./ps+ -c
Displays all processes with non-zero cpu usage
Yes, you can combine flags, but it doesnt work as you would expect. First users with name $USER will appear (ie - root), then processes, then non-zero processes. I didnt have time to get it working 'properly'



Heres the "man page"

Code:
NAME
		ps+  - display processes based on input criteria


SYNOPSIS

		ps+ [ -u USERNAME ] [ -p PROCESS ID ] [ -c ]



DESCRIPTION

Display information about PROCESSES being executed. No data is displayed if –upc are missing.

-u USERNAME
	Display processes owned by USERNAME

-p PROCESS ID
	Display processes with PID of PROCESS ID

-c
	Display processes with non-zero CPU usage


AUTHOR

Program written by <lowpro2k3>, program requirements written by <lowpro2k3's teacher>


REPORTING BUGS

Report bugs to 
<fp.unit@gmail.com> or
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Role Playing Server webwolf70 Linux - Games 0 09-17-2005 03:06 PM
Linux Support role? venkat_bommireddipal Linux - Certification 1 09-15-2005 01:31 PM
what is the role of /boot/config file? pyenos Linux - Software 1 03-03-2005 09:43 AM
just wondering.. what is the role of glib or glibc or whatever..? kublador Linux - Newbie 5 07-28-2003 01:17 PM
What's the role of ##*. ? Rex_chaos Linux - General 1 04-29-2002 08:54 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 10:05 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration