6

I have this line:

trap 'jobs -p | xargs kill -9' SIGINT SIGTERM EXIT

it is repeated in many bash shell scripts I have.

What is the best way to share this code? Can I call a bash function perhaps?

In reality, I am creating a framework and the user will be expected to write some glue shell scripts. What would be nice if the user's shell scripts could inherit from a base shell script, somehow. Or they could just call bash functions that pre-exist somehow.

The problem is that if I create a bash function like so:

// a.sh
function trap_and_kill_child_jobs {
    trap 'jobs -p | xargs kill -9' SIGINT SIGTERM EXIT
}

and call it from another script like so:

// b.sh
source ./a.sh
trap_and_kill_child_jobs
sh -c 'sleep 10000 &' &   # I want this process to be killed by `trap_and_kill_child_jobs`
./run-some-tests.js

the caller script (b.sh) does not actually experience the trap. The 'child jobs' created by b.sh will continue running.

  • 1
    "What would be nice if the user's shell scripts could inherit from a base shell script, somehow." Please don't use shell for this. It's not really a language. It IS for glue scripts, and nothing else. Every shell scripting "framework" I've seen (without exception) is (a) complex, (b) buggy, (c) a nightmare for the poor sap who gets to inherit and attempt to maintain it. – Wildcard Sep 06 '17 at 04:23
  • Yeah I hear ya, this is just for putting one shell script in between stuff, I doubt very many poor saps will be sapped. – Alexander Mills Sep 06 '17 at 04:34
  • @Wildcard can you speak to why calling the trap_and_kill_child_jobs function wouldn't invoke trap to actually work? – Alexander Mills Sep 06 '17 at 19:44
  • Without seeing the rest of your script, no, I can't. You don't show any child jobs being created. – Wildcard Sep 06 '17 at 19:51
  • Yeah I see, I updated the OP to show a job being created, hopefully it makes sense now – Alexander Mills Sep 06 '17 at 20:03
  • 1
    Thanks. You need to get into the details of how node works. I'm not a JS developer, but I expect what's going on is that node is forking and starting some other processes, and then exiting. So it won't show up in your "jobs" list at all. Again, a shell scripting framework is almost invariably code smell. You have architectural problems here; you're trying to use the shell to keep track of internal workings of JS when it really can't do that. – Wildcard Sep 06 '17 at 20:12
  • mmm node server.js is just starting a server that will listen on a port, the process will be kept alive indefinitely until it receives a signal. To elaborate, calling trap_and_kill_child_jobs works if the function is declared the same script, but not if the function is sourced from another script. By "works" I mean the nodejs server is killed by trap when the process that executed the bash script exits. – Alexander Mills Sep 06 '17 at 20:14
  • In that case I suggest you make some reproducible versions that illustrate what you're talking about. Perhaps the trap command really does work differently in functions. Try using the sleep command and perhaps sh -c 'sleep 10000 &' and so forth to get some jobs to start other processes. See if you can make the sleep commands get killed/not get killed. Again, see the fundamental philosophy of debugging—make it possible for someone else to look at the same phenomena you see. – Wildcard Sep 06 '17 at 20:37
  • So like Docker? :) Otherwise reproducible is very hard to do unless you have node.js on your system etc. But yeah I thought someone might know what I was talking about without the deets. I will change out the node server.js command for the sleep command you mentioned. – Alexander Mills Sep 06 '17 at 20:58
  • Minimal reproducible example. If it is really true that trap doesn't operate correctly/the same when run in a function sourced from another file (which I doubt is actually the case), it will be possible to reproduce this behavior without node.js at all, using only standard shell commands. – Wildcard Sep 06 '17 at 21:02
  • I agree, so that's why I changed it to sh instead of node in the example – Alexander Mills Sep 06 '17 at 21:04
  • run the trap, drop the bass – Alexander Mills Sep 07 '17 at 02:06

1 Answers1

4

It would be enough create a script file that contain your framework functions like this:

/tmp/framework.sh

# Define a serie of functions of your framework...
function framework_function_1() {
    echo "function 1 executed"
}

function framework_function_2() {
    echo "function 2 executed"  
}

# And put here anything you want to be executed right away (like the trap)
echo "framework.sh was executed"

Then include it in the rest of your scripts like this:

/tmp/b.sh

# Include the framework:
.  /tmp/framework.sh

echo "Script b.sh was executed"
# Calling a framework's function
framework_function_2

With this the execution of b.sh (and any other script including framework.sh) will be like:

$ /tmp/b.sh 
framework.sh was executed
Script b.sh was executed
function 2 executed

Note that . /tmp/framework.sh is the same as source /tmp/framework.sh.