3

I use shell job control to kick longer running tasks in to the background so I can continue while they churn through their data.

I can see the jobs I've got running with jobs, for example,

jobs
[2]-  Running                 su - root -c "..." &  (wd: /backup/rsnapshot)
[3]+  Running                 sleep 60 &

and I can control any one of these using the % jobspec syntax, such as fg %su or kill %3.

If I use jobs -l I get the PIDs too:

jobs -l
[2]- 31736 Running                 su - root -c "..." &  (wd: /backup/rsnapshot)
[3]+  2269 Running                 sleep 60 &

Using bash I've got jobs -x, but this isn't POSIX. Is this the only way to translate these % jobspec values into PIDs, or is there a sensible (better) alternative approach? What about for other shells?

jobs -x echo %3
2269

My target use case is for the % jobspec to be expanded transparently on the command line into the corresponding PID, so that in a command such as this the %2 would be seen by the command as 31736

pidtree %2    # pidtree 31736

This can be handled as jobs -x pidtree %2 but that's not as elegant or convenient.

I'd like at least one answer targeting bash, but contributions for other shells with job control are welcome, particularly if you have a POSIX solution.

Chris Davies
  • 116,213
  • 16
  • 160
  • 287
  • jobs -l or jobs -x can be piped to a bash action to get informations over the job like openned files & so on ; depending of your final objectives . for example cat /proc/25943/task/25943/statm and so on... Please edit the question with an use case. – francois P Aug 04 '20 at 11:07
  • tried out an example : cat /proc/$(jobs -l | sed 's/.*+ \(.*\) [A-Z].*/\1/; /[^0-9]/ d ')/statm answered 2451 1109 398 219 0 742 0 so sed substitution worked (not optimized at all to let you understand) – francois P Aug 04 '20 at 11:21
  • jobs -l or jobs -x is part of the Bourne Shell since before bash exists. Is your question about non-conforming shells? – schily Aug 04 '20 at 11:22
  • I suspect that roaima's next question will be what part of the SVID talks about jobs -x, because the SUS does not. (-: – JdeBP Aug 04 '20 at 12:06
  • @JdeBP Thanks for the hint. It is in the Boune Shell and I thought it would be in the Korn Shell but it is not. This is really strange. Well, the Bourne Shell is a nice shell for interactive use nowerdays. – schily Aug 04 '20 at 12:22
  • @roaima, I suppose parsing the output isn't an option? Something like jobs -l | perl -lne '/\[(\d+)\][-+]?\s+(\d+)\s+(\S+)\s+(\S.*)/; print "jobID:$1\tPID:$2\tstate:$3\tcomm:$4"'? – terdon Aug 04 '20 at 12:33
  • 1
    @schily jobs -x wasn't recognised by dash and it's not POSIX – Chris Davies Aug 04 '20 at 12:45
  • @francoisP use case added. Basically it's interactive use where I want the % jobspec to be translated (transparently if possibly) to the PID – Chris Davies Aug 04 '20 at 12:59

3 Answers3

2

On bash you might get close to that by providing a custom $PROMPT_COMMAND setting variables on-fly from the current list of jobs.

Proof-of-concept:

$ PROMPT_COMMAND='unset ${!j*}; eval "$(jobs -l | awk '\''{gsub("[^[:digit:] ]", ""); printf "j%d=%d\n", $1, $2}'\'')"'

You would then do pidtree $j2 instead of pidtree %2, assuming there are no numbers in $IFS or else you have to quote the $j2 thus requiring two keystrokes more.

LL3
  • 5,418
1

If you want this for a few commands only, then programmable completion would be an option. That is most probably not POSIX but at least available in several shells (and the underlying mechanism would not even have to be the same).

So you would type pidtree %2Tab and the shell would transform that into

pidtree 31736

I would consider that as elegant and convenient.

Disadvantage: Keeping existing completion configurations for those commands requires manual intervention (like: run your function first; if it does not match then make it call the original one).

_pidtree () {
    local job pid
    if [[ ${COMP_WORDS[COMP_CWORD]} =~ ^%[1-9][0-9]* ]]; then
        job="${COMP_WORDS[COMP_CWORD]:1}"
        pid="$(jobs -l | awk -v job="$job" '$1 ~ "^\\[" job "\\]" { print $2; }')"
        COMPREPLY=("$pid")
    fi
}

complete -F _pidtree pidtree

That works in bash. It could be done without awk, and it certainly can be done in a more compatible way.

Jobs which contain newlines could be a problem but as this is your interactive shell there seems not to be a risk that this could be exploited.

Hauke Laging
  • 90,279
0

Wouldn't it be easiest to just remember the PIDs when the backgroud jobs are created?

bash:

declare -a bgjobpids
[...]
do_some_thing &
pid=$!
index=${#bgjobpids[@]}
bgjobpids[index]=$pid
[...]
do_some_thing_else &
pid=$!
index=${#bgjobpids[@]}
bgjobpids[index]=$pid
Hauke Laging
  • 90,279