Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

How do I implement a wrapper script pstrace in bash that changes the interface of

[sudo] strace -c -p [PID]

to

[sudo] pstrace -c -p [PROCESS-NAME]

similar to how

killall [PROCESS-NAME]

is used. With completion and everything.

share|improve this question

2 Answers 2

Thy this:

ps auxw | grep [PROCESS-NAME] | awk '{print"-p " $2}' | xargs strace
share|improve this answer

Deceptively complicated requirements :-)

In two parts, firstly the pstrace wrapper script for strace, this uses pgrep for the name-to-PID operation.

#!/bin/bash

IFS=$' \t\n'

# process the arguments to find "-p procname", only support one instance though
for ((nn=1; nn<=$#; nn++)); do
    if [ "${!nn}" = "-p" ]; then
        :
    elif [ "$prev" = "-p" ]; then
        pname="${!nn}"
    else
        args+=( "${!nn}" )    # just copy 
    fi
    prev="${!nn}"
done

pids=()
if [ -n "$pname" ]; then
    # skip this shell's PID, which pgrep -f will match
    # note the use of exec to avoid picking up a matching subshell too
    # uncomment  && printf for pid/pname list
    while read pp pname; do 
         [ "$pp" != "$$" ] && pids+=($pp) # && printf "%6i %s\n" "$pp" "$pname"
    done < <(exec pgrep -l -f "${pname}")
fi

npids=${#pids[*]}

if [ $npids -eq 0 ]; then
    echo "No PIDs to trace."; exit 2
elif [ $npids -eq 1 ]; then
    args=( "${args[@]}" -p ${pids[0]} )
elif [ $npids -le 32 ]; then
    read -p "$npids PIDS found, enter Y to proceed: " yy
    [ "$yy" != "Y" ] && echo "Cancelled..." && exit 1
    args=( "${args[@]}" ${pids[@]/#/-p } ) 
else 
    echo "Too many PIDs to trace: $npids (max 32)."; exit 2
fi 

strace "${args[@]}"

For the second part I'll use bash programmable completion to complete processes by name, put this in your ~/.bash_profile or similar:

# process-name patterns to ignore
PROCIGNORE=( "^\[", "^-bash" )

_c8n_listprocs ()
{
    local cur prv ignore IFS nn mm
    prv=${COMP_WORDS[COMP_CWORD-1]}
    cur=${COMP_WORDS[COMP_CWORD]}

    case "$prv" in
        '-p') 
              IFS=$'\n' COMPREPLY=( $(ps axwwo "args") ) IFS=$' \t\n'
              COMPREPLY=(${COMPREPLY[*]// */})  # remove arguments

              ignore="0" # ps header
              for ((nn=1; nn<${#COMPREPLY[*]}; nn++)); do
                  # filter by (partially) typed name in cur
                  # use  " =~ ^$cur " for prefix match, without ^ it's substr match
                  [[ -n "$cur"  &&  ! "${COMPREPLY[$nn]}" =~ $cur ]] && {
                      ignore="$nn $ignore"
                  } || { 
                      # skip names matching PROCIGNORE[]
                      for ((mm=0; mm<${#PROCIGNORE[*]}; mm++)); do
                          [[ "${COMPREPLY[$nn]}" =~ ${PROCIGNORE[$mm]} ]] && 
                              ignore="$nn $ignore"
                      done
                  }
              done
              # remove unwanted, in reverse index order
              for nn in $ignore; do unset COMPREPLY[$nn]; done

              ;;
        *)    COMPREPLY=()
              ;;
    esac
}
complete -F _c8n_listprocs pstrace

Tested & used on linux with bash-3.x and bash-4.x. ps options may need tweaking on non-Linux platforms, should also support truss with a one-line change.

Limitations include:

  • no correct escaping of kernel-thread like process [names], this will cause pgrep to (likely) not do what you want
  • mismatched of processes with space in the name ("args" is used instead of "comm" so that /paths can be used , where available)
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.