2

I'm a newbie on bash scripts and would like to ask what's best/easiest way to achieve the following:

  1. run a command in parallel (e.g. run a nodejs web app); the output of the command should be written to console as usual
  2. wait until a specific string occurs in output (e.g. "DB initialized")
  3. then run another command (e.g. command to create some DB data)
  4. afterwards wait until the command from 1. ends

This is my current script that does the waiting by sleep but of course this is just a workaround:

# parallel command
npm run start &

wait for specific output

sleep 15s

do something

node_modules/.bin/knex seed:run

wait for parallel command to exit

wait

I found a similar questions but the output of command executed in parallel is not printed to console I think.

Background: The script is the entry point of a docker image based on the alpine variant of nodejs image.

  • Do you have specific reasons for running npm run start asynchronously? Are you starting several concurrent instances of it? If yes, will "DB initialized" be emitted several times? And will it be required to trigger node_modules/.bin/knex every time? – fra-san Sep 27 '20 at 22:53
  • @fra-san I run it asynchronously to do the knex command when DB is initialized. It's only one instance of web app. The knex command should be executed one time per script run and "DB intialized" will occur only one time. The script is ran as entry point for a docker image. – Steffen Harbich Sep 28 '20 at 06:41
  • You may also be interested in https://unix.stackexchange.com/q/5277/315749 – fra-san Oct 12 '20 at 08:24

1 Answers1

1

One of the possible ways to achieve that:

npm run start |
  tee /dev/tty | {
    grep -q "DB initialized" && node_modules/.bin/knex seed:run
    cat >/dev/null
  }

tee /dev/tty duplicates npm's output and sends it to both the terminal and the command that follows in the pipeline.

grep -q silently exits at the first match with 0 as its status; this triggers the next command in the "AND" list (node_modules/.bin/knex seed:run).

cat /dev/null is there just to keep the pipe open: grep -q exiting would cause tee to receive a SIGPIPE signal when trying to write further data to the pipe and exit in turn. This cat prevents that by reading from the pipe until npm and tee exit.

There is no need to run npm asynchronously: all the components of a pipeline run concurrently and the only thing you want to ensure here is that none of them exits before the slower one completes.

Note that /dev/tty may not be available if this snippet is run in the context of a process with no controlling terminal. An alternative, assuming the standard streams are also accessible in the /dev/fd/n form on your system, is:

npm run start | {
  tee /dev/fd/9 | {
    grep -q "DB initialized" && node_modules/.bin/knex seed:run
    cat >/dev/null
  }
} 9>&1

The 9 file descriptor (arbitrarily chosen) is created as a copy of the file the whole pipeline's standard output is connected to, making it available as /dev/fd/9 to any command in the outer { ... } list.

fra-san
  • 10,205
  • 2
  • 22
  • 43