0

I am running the below directly in bash command line prompt:

$ PIDS= ;while read name; do (cd $name; npm install) & echo started install; PIDS+=($!); done < <(\ls); for pid in ${PIDS[@]}; do wait $pid; done;

And I get this:

-bash: !: event not found

I assume the ! symbol is being used to do command history substitution instead of seeing "$!" as a variable first.


How do I get pid of last background process if running directly on bash command line?

  • Can you give us the true command line you used? This one has obvious syntax errors. – xhienne Aug 15 '17 at 13:36
  • I corrected syntax problems. If you replace "npm install" with "echo npm install" and replace "$!" with a non-existent pid (like 293847293847) then it should run for you – Alexander Bird Aug 15 '17 at 13:41
  • I can't reproduce it with the command line you give. What is you version of bash? – xhienne Aug 15 '17 at 13:50
  • 1
    Don't parsels -- try for name in * instead of while read name ... < <(\ls) -- if you want to limit the files to only directories do for dir in */ – glenn jackman Aug 15 '17 at 14:22
  • 1
    ` $ bash --version GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15) Copyright (C) 2007 Free Software Foundation, Inc.`. I wonder if older Bash and on a mac is problematic here – Alexander Bird Aug 15 '17 at 18:59
4

I haven't been able to reproduce your problem with bash 4.3 and 4.4, so here is a generic answer.

Your problem is triggered by the ! in PIDS+=($!), ! being the start of history substitution (which is enabled by default with an interactive shell).

Either disable history substitution with set +H, or quote the ! (not desirable here because of the preceding $) or add a space after !. Bash manual reads:

! Start a history substitution, except when followed by a blank, newline, carriage return, = or ( (when the extglob shell option is enabled using the shopt builtin)

Your command line would then become:

PIDS=()
while read name; do
    (cd "$name"; npm install) &
    echo started install
    PIDS+=( $! )
done < <(\ls)
wait "${PIDS[@]}"

Notes:

  • You are not limited to one line of code here
  • I have added proper quotes around variables
  • You can wait for several PIDs at once; I fixed that
1

From the bash changelog:

This document details the changes between this version, bash-4.2-alpha, and the previous version, bash-4.1-release. (…)

3. New Features in Bash (…)

u. History expansion no longer expands the $! variable expansion.

To cope with older versions of bash, change PIDS+=($!) to PIDS+=($! ). The extra space ensures that bash will never think that this ! starts a history reference.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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