88

I'm starting a number of screens in a bash script, then running django's runserver command in each of them. I'd like to be able to programmatically stop them all as well, which requires me to send Control+c to runserver.

How can I send these keystrokes from my bash script?

Adrian Frühwirth
  • 42,970
  • 10
  • 60
  • 71
ghickman
  • 5,893
  • 9
  • 42
  • 51
  • See also http://stackoverflow.com/questions/14696427/how-can-bash-script-do-the-equivalent-of-ctrl-c-to-a-background-task – tripleee Sep 26 '16 at 14:25

5 Answers5

107

Ctrl+C sends a SIGINT signal.

kill -INT <pid> sends a SIGINT signal too:

# Terminates the program (like Ctrl+C)
kill -INT 888
# Force kill
kill -9 888

Assuming 888 is your process ID.


Note that kill 888 sends a SIGTERM signal, which is slightly different, but will also ask for the program to stop. So if you know what you are doing (no handler bound to SIGINT in the program), a simple kill is enough.

To get the PID of the last command launched in your script, use $! :

# Launch script in background
./my_script.sh &
# Get its PID
PID=$!
# Wait for 2 seconds
sleep 2
# Kill it
kill $PID
Matthieu Napoli
  • 48,448
  • 45
  • 173
  • 261
  • depends if you want to send the command ctrl+c to some other process as input. – j0h Jul 16 '16 at 14:32
  • 3
    ⚠️`ctrl`+`c` is not exactly the same as `kill -SIGINT `. The former sends the `INT` signal to the group and the later only to the given ``. [See this question](https://stackoverflow.com/a/8406413/6320039). – Ulysse BN Aug 04 '20 at 07:40
15

CTRL-C generally sends a SIGINT signal to the process so you can simply do:

kill -INT <processID>

from the command line (or a script), to affect the specific processID.

I say "generally" because, as with most of UNIX, this is near infinitely configurable. If you execute stty -a, you can see which key sequence is tied to the intr signal. This will probably be CTRL-C but that key sequence may be mapped to something else entirely.


The following script shows this in action (albeit with TERM rather than INT since sleep doesn't react to INT in my environment):

#!/usr/bin/env bash

sleep 3600 &
pid=$!
sleep 5

echo ===
echo PID is $pid, before kill:
ps -ef | grep -E "PPID|$pid" | sed 's/^/   /'
echo ===

( kill -TERM $pid ) 2>&1
sleep 5

echo ===
echo PID is $pid, after kill:
ps -ef | grep -E "PPID|$pid" | sed 's/^/   /'
echo ===

It basically starts an hour-log sleep process and grabs its process ID. It then outputs the relevant process details before killing the process.

After a small wait, it then checks the process table to see if the process has gone. As you can see from the output of the script, it is indeed gone:

===
PID is 28380, before kill:
   UID   PID     PPID    TTY     STIME      COMMAND
   pax   28380   24652   tty42   09:26:49   /bin/sleep
===
./qq.sh: line 12: 28380 Terminated              sleep 3600
===
PID is 28380, after kill:
   UID   PID     PPID    TTY     STIME      COMMAND
===
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
14

ctrl+c and kill -INT <pid> are not exactly the same

To emulate ctrl+c we need to first understand the difference.

kill -INT <pid> will send the INT signal to a given process (found with its pid).

ctrl+c is mapped to the intr special character which when received by the terminal should send INT to the foreground process group of that terminal. You can emulate that by targetting the group of your given <pid>. It can be done by prepending a - before the <pid> in the kill command. Hence the command you want is:

kill -INT -<pid>

You can test it pretty easily with a script:

#!/usr/bin/env ruby

fork {
    trap(:INT) {
        puts 'signal received in child!'
        exit
    }
    sleep 1_000
}

puts "run `kill -INT -#{Process.pid}` in any other terminal window."
Process.wait

Sources:

Dirk Herrmann
  • 5,550
  • 1
  • 21
  • 47
Ulysse BN
  • 10,116
  • 7
  • 54
  • 82
0

You can get the PID of a particular process like MySQL by using following commands:

ps -e | pgrep mysql

This command will give you the PID of MySQL process. e.g, 13954

Now, type following command on terminal.

kill -9 13954

This will kill the process of MySQL.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • Don't use `kill -9`, especially not if you don't know what you are doing. The question asks how to send SIGINT, not SIGKILL, anyway. Also, piping `ps` output to `pgrep` is obviously just useless. – tripleee May 26 '23 at 08:01
-1
    pgrep -f process_name > any_file_name
    sed -i 's/^/kill /' any_file_name
    chmod 777 any_file_name
    ./any_file_name

for example 'pgrep -f firefox' will grep the PID of running 'firefox' and will save this PID to a file called 'any_file_name'. 'sed' command will add the 'kill' in the beginning of the PID number in 'any_file_name' file. Third line will make 'any_file_name' file executable. Now forth line will kill the PID available in the file 'any_file_name'. Writing the above four lines in a file and executing that file can do the control-C. Working absolutely fine for me.

user2176228
  • 327
  • 3
  • 10
  • Whatever you are hoping to accomplish, **`chmod 777` is *wrong* and *dangerous.*** You absolutely do not want to grant write access to executable or system files to all users under any circumstances. You will want to revert to sane permissions ASAP (for your use case, probably `chmod 755`) and learn about the Unix permissions model before you try to use it again. If this happened on a system with Internet access, check whether an intruder could have exploited this to escalate their privileges. – tripleee May 26 '23 at 07:59
  • Anyway, this is a horribly complex way to say `pgrep -f process_name | xargs -r kill` – tripleee May 26 '23 at 08:00