mfproc (4036B)
1 #!/bin/sh 2 3 # ΧΡΗΣΤΟΣ ΜΑΡΓΙΩΛΗΣ - [REDACTED] 4 5 # Exit codes: 6 # 0 Success 7 # 1 No user 8 # 2 No process 9 # 3 Usage 10 11 main() { 12 # Using getopts(1) because it's easier to parse 13 # optional command line options. getopts(1) works 14 # by passing it all the available options. If an option 15 # is followed by a ':', that means that getopts(1) is 16 # expecting an argument as well. If no argument is passed, 17 # it exits with an error code of 1. In this case I've 18 # passed it the string "u:s:" which means that the options 19 # are '-u' and '-s' and both of them take an argument. 20 # The argument is always stored in the `OPTARG` environmental 21 # variable. 22 while getopts u:s: arg; do 23 case "${arg}" in 24 # In order for `username` to really be a user's name, it 25 # has to exist in `/etc/passwd`. 26 u) test ! -z "$(grep -w "^${OPTARG}" /etc/passwd)" || 27 err "'${OPTARG}' is not in /etc/passwd" 1 28 # Get UID from username. 29 uid=$(id -u ${OPTARG}) ;; 30 s) state="${OPTARG}" 31 # `state` needs to be either S, R or Z in order 32 # to be valid. 33 validstate ${state} || usage ;; 34 *) usage 35 esac 36 done 37 38 count=0 39 # Print header. expand(1) will will expand \t to 16 spaces 40 # in order for the output to be aligned. 41 printf "Name\tPID\tPPID\tUID\tGID\tState\n" | expand -t 16 42 43 # Parse each process in `/proc`. This is a slow way to parse it though. 44 for proc in /proc/*/status; do 45 procuid=$(getfield "Uid:\s*${uid}" ${proc}) 46 procgid=$(getfield "Gid" ${proc}) 47 # If the `-u` option was passed, ignore every UID that 48 # does not match the UID we gave it as an argument. 49 # We're using a `continue` command here so that it 50 # stop searching for anything else in this file since 51 # we don't need it. 52 test -z ${uid} || [ "${procuid}" == "${uid}" ] || continue 53 54 procstate=$(getfield "State:\s*${state}" ${proc}) 55 56 # Ignore any state other than S|R|Z 57 validstate ${procstate} || continue 58 59 # If the `-s` option was passed, ignore every state that does 60 # not match the one we gave it as an argument. 61 test -z ${state} || [ "${procstate}" == "${state}" ] || continue 62 63 procname=$(getfield "Name" ${proc}) 64 procpid=$(getfield "Pid" ${proc}) 65 procppid=$(getfield "PPid" ${proc}) 66 67 # Print everything in a top(1)-like format. 68 printf "%s\t%s\t%s\t%s\t%s\t%s\n" \ 69 ${procname} ${procpid} ${procppid} ${procuid} ${procgid} ${procstate} | 70 expand -t 16 71 72 # Increment counter by 1 to keep track of how many processes 73 # we have printed. If `count` is 0 after the loop, the script 74 # will exit with error code 2, as is done below. 75 count=$(expr ${count} + 1) 76 done 77 78 # We didn't print any process 79 test ${count} -eq 0 && exit 2 80 81 # Success! 82 exit 0 83 } 84 85 usage() { 86 # `{0##*/}` means the first argument (i.e the script's name) with 87 # its path stripped, if it exists. 88 echo "usage: ${0##*/} [-u username] [-s S|R|Z]" 1>&2 89 exit 3 90 } 91 92 err() { 93 echo "${0##*/}: $@" 1>&2 94 exit ${2} 95 } 96 97 # Check if process is either S, R or Z. 98 validstate() { 99 [ "${1}" = "S" ] || [ "${1}" = "R" ] || [ "${1}" = "Z" ] 100 } 101 102 # Get wanted field. grep(1) will return the line 103 # that begins with the first argument we passed. 104 # awk(1) is used to extract the 2nd field in the string. 105 getfield() { 106 grep -w "^${1}" ${2} | awk '{print $2}' 107 } 108 109 # Pass all command line arguments to `main`. 110 main "$@"