Search code examples
linuxparsingshelloutputterminate

Linux shell kill command while still running based on output


I have a set of C++ projects in Code::Blocks and a script that builds all of them. The script looks as following:

codeblocks --build --target="release32" project1.cbp
codeblocks --build --target="release32" project2.cbp
codeblocks --build --target="release32" project3.cbp
...

Sometimes when building one of the projects, Code::Blocks fails to finish the process with an error: *** glibc detected *** codeblocks: corrupted double-linked list. This has nothing to do with the build being failed, this is just a bug in codeblocks, see here: https://bugs.launchpad.net/ubuntu/+source/codeblocks/+bug/764728 or here: http://forums.codeblocks.org/index.php?topic=16883.0

A workaround that I want to do is to parse the output of the command while it's still running, find the string "codeblocks: corrupted double-linked list", and kill the codeblocks to let the script build other projects and finish.

How can I do that?


Solution

  • A little hacky but will this work for you (bash)?

    This is just to simulate the codeblocks binary (./codeblocks):

    $ cat codeblocks
    #!/bin/bash
    
    num=$RANDOM
    ((num %= 2))
    
    case "$num" in
            0)
                    echo "codeblocks finished successfully: $*"
                    exit 0
                    ;;
            1)
                    echo "*** glibc detected *** codeblocks: corrupted double-linked list" 1>&2
                    while true; do
                            sleep 1
                    done
                    ;;
    esac
    

    This is the actual test script (test.sh):

    $ cat test.sh
    #!/bin/bash
    
    run_codeblocks()
    {
            until (
                    subshell_pid=$BASHPID
                    echo "trying to run 'codeblocks $*'"
                    ./codeblocks "$@" 2>&1 | while read line; do
                            echo "[${line}]"
                            [[ ${line} == *"*** glibc detected ***"* ]] && kill $subshell_pid
                    done
                    return 0
            ); do
                    :
            done
    }
    
    echo "running codeblocks ..."
    run_codeblocks 1
    run_codeblocks 2
    run_codeblocks 3
    echo "... done"
    

    You wrap the call to codeblocks in a subshell, grep its output and if a line matches the error you stated kills the subshell. Basically exactly what you described.

    $ ./test.sh
    running codeblocks ...
    trying to run 'codeblocks 1'
    [codeblocks finished successfully: 1]
    trying to run 'codeblocks 2'
    [*** glibc detected *** codeblocks: corrupted double-linked list]
    ./test.sh: line 4: 29889 Terminated              ( subshell_pid=$BASHPID; echo "trying to run 'codeblocks $*'"; ./codeblocks "$@" 2>&1 | while read line; do
        echo "[${line}]"; [[ ${line} == *"*** glibc detected ***"* ]] && kill $subshell_pid; return 0;
    done )
    trying to run 'codeblocks 2'
    [*** glibc detected *** codeblocks: corrupted double-linked list]
    ./test.sh: line 4: 29892 Terminated              ( subshell_pid=$BASHPID; echo "trying to run 'codeblocks $*'"; ./codeblocks "$@" 2>&1 | while read line; do
        echo "[${line}]"; [[ ${line} == *"*** glibc detected ***"* ]] && kill $subshell_pid; return 0;
    done )
    trying to run 'codeblocks 2'
    [codeblocks finished successfully: 2]
    trying to run 'codeblocks 3'
    [*** glibc detected *** codeblocks: corrupted double-linked list]
    ./test.sh: line 4: 29903 Terminated              ( subshell_pid=$BASHPID; echo "trying to run 'codeblocks $*'"; ./codeblocks "$@" 2>&1 | while read line; do
        echo "[${line}]"; [[ ${line} == *"*** glibc detected ***"* ]] && kill $subshell_pid; return 0;
    done )
    trying to run 'codeblocks 3'
    [codeblocks finished successfully: 3]
    ... done