Starting a process over ssh using bash and then killing it on sigint

前端 未结 5 676

I want to start a couple of jobs on different machines using ssh. If the user then interrupts the main script I want to shut down all the jobs gracefully.

Here is a shor

5条回答
  •  误落风尘
    2021-02-02 15:52

    Referencing the answer by lhunath and https://unix.stackexchange.com/questions/71205/background-process-pipe-input I came up with this script

    run.sh:

    #/bin/bash
    log="log"                                                                                 
    eval "$@" \&                                                                              
    PID=$!                                                                                    
    echo "running" "$@" "in PID $PID"> $log                                                   
    { (cat <&3 3<&- >/dev/null; kill $PID; echo "killed" >> $log) & } 3<&0                              
    trap "echo EXIT >> $log" EXIT                                                             
    wait $PID
    

    The difference being that this version kills the process when the connection is closed, but also returns the exit code of the command when it runs to completion.

     $ ssh localhost ./run.sh true; echo $?; cat log
     0
     running true in PID 19247
     EXIT
    
     $ ssh localhost ./run.sh false; echo $?; cat log
     1
     running false in PID 19298
     EXIT
    
     $ ssh localhost ./run.sh sleep 99; echo $?; cat log
     ^C130
     running sleep 99 in PID 20499
     killed
     EXIT
    
     $ ssh localhost ./run.sh sleep 2; echo $?; cat log
     0
     running sleep 2 in PID 20556
     EXIT
    

    For a one-liner:

     ssh localhost "sleep 99 & PID=\$!; { (cat <&3 3<&- >/dev/null; kill \$PID) & } 3<&0; wait \$PID"
    

    For convenience:

     HUP_KILL="& PID=\$!; { (cat <&3 3<&- >/dev/null; kill \$PID) & } 3<&0; wait \$PID"
     ssh localhost "sleep 99 $HUP_KILL"
    

    Note: kill 0 may be preferred to kill $PID depending on the behavior needed with regard to spawned child processes. You can also kill -HUP or kill -INT if you desire.

    Update: A secondary job control channel is better than reading from stdin.

    ssh -n -R9002:localhost:8001 -L8001:localhost:9001 localhost ./test.sh sleep 2
    

    Set job control mode and monitor the job control channel:

    set -m
    trap "kill %1 %2 %3" EXIT
    (sleep infinity | netcat -l 127.0.0.1 9001) &
    (netcat -d 127.0.0.1 9002; kill -INT $$) &
    "$@" &
    wait %3
    

    Finally, here's another approach and a reference to a bug filed on openssh: https://bugzilla.mindrot.org/show_bug.cgi?id=396#c14

    This is the best way I have found to do this. You want something on the server side that attempts to read stdin and then kills the process group when that fails, but you also want a stdin on the client side that blocks until the server side process is done and will not leave lingering processes like <(sleep infinity) might.

    ssh localhost "sleep 99 < <(cat; kill -INT 0)" <&1
    

    It doesn't actually seem to redirect stdout anywhere but it does function as a blocking input and avoids capturing keystrokes.

提交回复
热议问题