Skip to content

Instantly share code, notes, and snippets.

@norswap
Last active February 27, 2024 20:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save norswap/3506d2b46102c2f32f18acced0ecd798 to your computer and use it in GitHub Desktop.
Save norswap/3506d2b46102c2f32f18acced0ecd798 to your computer and use it in GitHub Desktop.
norswap's process tool
#!/bin/bash
# ps version: "ps from procps-ng 3.3.17"
### --- Constants ---
SCRIPT_CMD='script -q /dev/null'
TAIL_CMD='tail -n +2'
### --- Parameters ---
ALL='' # flag to match all users process
CASE='-i' # flag for case-insensitive matches
KILL=0 # if 1, kill matched processes
# ps fields to match on
MATCH_FIELDS='-o pid,command'
# ps fields to output
OUTPUT_FIELDS='-o pid,command'
# command used to strip the header (or not)
STRIP_HEADER="$TAIL_CMD"
if [[ -t 1 ]]; then
COLOR='--color=always' # whether to colorize output
CLIP="cut -c -$(tput cols)" # command to clip command names (or not)
else
COLOR='--color=none'
CLIP='\cat'
fi
### --- Help ---
help() {
echo -e "Usage: nps <opts or search terms> [-- <search terms>]\n" \
" Print processes.\n" \
" -a: include processes from [a]ll users\n" \
" -s: case-[s]ensitive match\n" \
" -k: [k]ill the mached processes\n" \
" -h: print this [h]elp and exit\n" \
" -l: print the [l]ist of selectable fields and exit\n" \
" -m <fields>: specify comma-separated list of field to [m]atch on\n" \
" If specifying only '-m pid', only matches entired PIDs.\n" \
" The PID is always also included in the matched fields (as additional\n" \
" first field). If specifying only '-m pid', only matches entired PIDs. \n" \
" -o <fields>: specify comma-separated list of fields to [o]utput\n" \
" -v: [v]erbose output, equivalent to\n" \
" -o 'user,pid,ppid,pgid,ses ,jobc,state,tt,time,command' \n" \
" --color: [c]olorize output even when piping to programs\n" \
" --header: include fields (columns) header in output\n" \
" --full: output full command name even if stdout is a terminal\n" \
" --clip: output clipped command name even if stdout is not a terminal\n" \
" --version: print version and exit\n";
}
### --- Arg Parsing ---
POSITIONAL=()
while [[ $# -gt 0 ]]; do case "$1" in
-a) ALL='-a';;
-s) CASE='';;
-v) OUTPUT_FIELDS="-o user,pid,ppid,pgid,sess,jobc,state,tt,time,command";;
-o) OUTPUT_FIELDS="-o $2"; shift;;
-m) MATCH_FIELDS="-o $2"; shift;;
-k) KILL=1;;
-l) ps L; exit;;
-h|--help) help; exit;;
--) shift; break;;
--color) COLOR='--color=always';;
--header) STRIP_HEADER='cat';;
--full) CLIP='';;
--clip) CLIP="$SCRIPT_CMD";;
--version) echo 2022.06.16-linux; exit;;
-*) help; exit 1;;
*) POSITIONAL+=("$1");;
esac
shift
done
set -- "${POSITIONAL[@]}" "$@" # restore positional parameters
if [[ "$KILL" == "1" && (-z "$*" || "$*" == " ") ]]; then
echo "Attempting to kill with no search string or single space search string."
exit 1
fi
### --- Match PIDs ---
filter_header_and_grep() {
$TAIL_CMD | grep -v "$(which nps)" | grep -v " grep "
}
select_pids() {
grep $CASE -E -- "$*" | awk '{ print $1 }'
}
if [[ "$MATCH_FIELDS" == "-o pid" ]]; then
# if matching on PID only, only match whole PIDs
MATCH_FIELDS=-"p $*"
else
# include pid at start
MATCH_FIELDS="-o pid,${MATCH_FIELDS#-o }"
fi
# find matching PID
PIDS="$(ps -x $ALL $MATCH_FIELDS | filter_header_and_grep | select_pids "$*")"
# when no process is found: exit with 1, or with 0 when the -k flag is set
[[ "$PIDS" == '' ]] && { [[ $KILL == 1 ]]; exit $?; }
### --- Output ---
highlight_matches() {
if [[ "$*" == '' ]]; then cat; else grep $COLOR $CASE -E "($*)|$"; fi
}
# print with requested fields + highlighting
# the tr command cleans up the script command output
ps $OUTPUT_FIELDS -p $PIDS | tr -d '\r' | $STRIP_HEADER | highlight_matches "$*" | cut -c -$(tput cols)
EXIT=$?
### --- Kill ---
# kill matched processes if requested
if [[ $KILL == 1 ]]; then
kill -9 $PIDS
EXIT=$?
fi
exit $EXIT
#!/bin/bash
### --- Constants ---
SCRIPT_CMD='script -q /dev/null'
TAIL_CMD='tail -n +2'
### --- Parameters ---
ALL='' # flag to match all users process
CASE='-i' # flag for case-insensitive matches
KILL=0 # if 1, kill matched processes
# ps fields to match on
MATCH_FIELDS='-o pid,command'
# ps fields to output
OUTPUT_FIELDS='-o pid,command'
# command used to strip the header (or not)
STRIP_HEADER="$TAIL_CMD"
if [[ -t 1 ]]; then
COLOR='--color=always' # whether to colorize output
CLIP="$SCRIPT_CMD" # command to clip command names (or not)
else
COLOR='--color=none'
CLIP=''
fi
### --- Help ---
help() {
echo -e "Usage: nps <opts or search terms> [-- <search terms>]\n" \
" Print processes.\n" \
" -a: include processes from [a]ll users\n" \
" -s: case-[s]ensitive match\n" \
" -k: [k]ill the mached processes\n" \
" -h: print this [h]elp and exit\n" \
" -l: print the [l]ist of selectable fields and exit\n" \
" -m <fields>: specify comma-separated list of field to [m]atch on\n" \
" If specifying only '-m pid', only matches entired PIDs.\n" \
" The PID is always also included in the matched fields (as additional\n" \
" first field). If specifying only '-m pid', only matches entired PIDs. \n" \
" -o <fields>: specify comma-separated list of fields to [o]utput\n" \
" -v: [v]erbose output, equivalent to\n" \
" -o 'user, pid, ppid, pgid, sess ,jobc, state, tt, time, command' \n" \
" --color: [c]olorize output even when piping to programs\n" \
" --header: include fields (columns) header in output\n" \
" --full: output full command name even if stdout is a terminal\n" \
" --clip: output clipped command name even if stdout is not a terminal\n" \
" --version: print version and exit\n";
}
### --- Arg Parsing ---
POSITIONAL=()
while [[ $# -gt 0 ]]; do case "$1" in
-a) ALL='-a';;
-s) CASE='';;
-v) OUTPUT_FIELDS="-o 'user, pid, ppid, pgid, sess, jobc, state, tt, time, command'";;
-o) OUTPUT_FIELDS="-o $2"; shift;;
-m) MATCH_FIELDS="-o $2"; shift;;
-k) KILL=1;;
-l) ps -L; exit;;
-h|--help) help; exit;;
--) shift; break;;
--color) COLOR='--color=always';;
--header) STRIP_HEADER='cat';;
--full) CLIP='';;
--clip) CLIP="$SCRIPT_CMD";;
--version) echo 2020.06.16-mac; exit;;
-*) help; exit 1;;
*) POSITIONAL+=("$1");;
esac
shift
done
set -- "${POSITIONAL[@]}" "$@" # restore positional parameters
if [[ "$KILL" == "1" && (-z "$*" || "$*" == " ") ]]; then
echo "Attempting to kill with no search string or single space search string."
exit 1
fi
### --- Match PIDs ---
filter_header_and_grep() {
$TAIL_CMD | grep -v "$(which nps)" | grep -v " grep "
}
select_pids() {
grep $CASE -E -- "$*" | awk '{ print $1 }'
}
if [[ "$MATCH_FIELDS" == "-o pid" ]]; then
# if matching on PID only, only match whole PIDs
MATCH_FIELDS=-"p $*"
else
# include pid at start
MATCH_FIELDS="-o pid,${MATCH_FIELDS#-o }"
fi
# find matching PIDs
PIDS="$(ps -x $ALL "$MATCH_FIELDS" | filter_header_and_grep | select_pids "$*")"
# when no process is found: exit with 1, or with 0 when the -k flag is set
[[ "$PIDS" == '' ]] && { [[ $KILL == 1 ]]; exit $?; }
### --- Output ---
highlight_matches() {
if [[ "$*" == '' ]]; then cat; else grep $COLOR $CASE -E "($*)|$"; fi
}
# print with requested fields + highlighting
# the tr command cleans up the script command output
$CLIP ps -x "$OUTPUT_FIELDS" -p $PIDS | tr -d '\r' | $STRIP_HEADER | highlight_matches "$*"
EXIT=$?
### --- Kill ---
# kill matched processes if requested
if [[ $KILL == 1 ]]; then
kill -9 $PIDS
EXIT=$?
fi
exit $EXIT
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment