0

I am using shell trying to capture list of background jobs:

sleep 11 | tee log_1.txt &
sleep 12 | tee log_2.txt &

jobs -p # this returns PIDs

X=$(jobs -p)
echo ${X}

Then I start it like this: sh ./script.sh

But for some reason I get nothing.

Trying to debug it I see that jobs -p actually returns the list of items.

Something like X=$(ls -al) also works as expected and the content is captured.

SmxCde
  • 5,053
  • 7
  • 25
  • 45
  • You tagged this `bash` but you're running it with `sh`. Which is it? – that other guy Jul 29 '18 at 01:13
  • Possible duplicate of [How can I assign a value from a file to a variable in UNIX sh shell?](https://stackoverflow.com/q/26590554/608639). The short answer is, use backticks. – jww Jul 29 '18 at 01:13
  • @thatotherguy, yes sorry, probably I should not have don it but I worried that the question wont get enough attention. You are right, it is `/bin/dash`. – SmxCde Jul 29 '18 at 23:43
  • @jww, thank you for the reference, but backticks for some reason do not work with `jobs -p`, something like `ls -al` works but not `jobs`. I wonder what is special about that command. – SmxCde Jul 29 '18 at 23:47
  • `jobs` is an internal command (no subprocess knows the list of processes the shell has given status of process group leaders) so it's specific of the shell you are using. Once said that, you can expect different behaviour from different shells to the `jobs -p` command. – Luis Colorado Jul 30 '18 at 19:12

2 Answers2

2

Your code works fine in bash:

$ bash myfile
10715
10717
10715 10717
$

But it does fail in dash, which is probably what your sh is:

$ dash myfile
11048
11046

$

(see Why does my bash code fail when I run it with sh?).

dash is within its rights to behave this way (and Bash is arguably wrong) because POSIX specifies that "The jobs utility shall display the status of jobs that were started in the current shell environment", and you have created a new shell environment through your use of the subshelling $(...).

If you want to do it in a sh compatible way you can redirect to a temp file and read that instead. Alternatively, you can collect the pids of each started background job using $!.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • Thank you for the answer, I think redirection to a file will do it. Though I switched to `bash` instead, I do not have much experience in Linux scripting and many things confuse me, probably I should have though about differences between the two before writing this :) Anyway, I ended up with code like this (\ is next line): my-app | tee my.log & \ read -a PROCESSES <<< $(jobs -p) \ PROC=(${PROCESSES[@]}) \ PID0=${PROC[0]}; \ PID1=${PROC[1]}; \ – SmxCde Jul 29 '18 at 23:53
  • I could not use `$!` directly because since I pipe to `tee` I would get tee's PID. Also I should mention that I needed to start two apps in the background. One of the options I considered was using `2>&1 > my.log` with following `tail -f my.log &` instead of `| tee ...`, but that's too much hassle, since I need to do it twice and then kill two more processes. – SmxCde Jul 29 '18 at 23:58
  • Also, POSIX doesn't force compliment with job control. As optional, any vendor is free to include job control or not. – Luis Colorado Jul 30 '18 at 19:07
0

In subshells, jobs -p will print only the active jobs. This means that:

true &
sleep 2 &
sleep 1
jobs=$(jobs -p)

will only capture the second job (since the first one will be finished).

To capture all jobs, including jobs finished but not waited for (zombies), do this:

# all jobs, simple variable
read jobs < <(jobs -p)

# all jobs, array variable
readarray -t jobs < <(jobs -p)
alecov
  • 4,882
  • 2
  • 29
  • 55