🌐
Raspberry Pi Forums
forums.raspberrypi.com › board index › programming › python
Ctrl-c in python script - Raspberry Pi Forums
We use optional cookies, as detailed ... wrote:How do I do the EXACT equivalent of ctrl-c in the linux terminal, in a python script? Send etx (end of text) or hex 03 or '\x03'....
🌐
JBI Training
jbinternational.co.uk › article › view › 3649
How to Send Ctrl-C to Interrupt and Stop a Python Script
November 3, 2023 - We'll explain what happens when ... KeyboardInterrupt. Pressing Ctrl-C while a Python script is running sends a SIGINT (signal interrupt) to the Python interpreter, telling it to immediately stop whatever it's doing....
🌐
Reddit
reddit.com › r/python › windows command line automation - ctrl-c question
r/Python on Reddit: Windows command line automation - Ctrl-C question
May 6, 2013 -

I have a question about sending "ctrl-c" type events to stdin on Windows. I googled around and even StackOverflow was full of dead ends.

Let's consider a simple program:

import subprocess

TSHARK_PATH = "C:\\Program Files\\Wireshark\\tshark.exe"
OUTPUT_FILE_NAME = "captured_packets.pcap"
OUTPUT_DIR = "C:\\captured_packets_dir\\"
INTERFACE_NO = "1" #as per "tshark -D"
PACKET_FILTER = "icmp" # just an example
OUTPUT_FILE_SIZE_LIMIT = "200" #in kB

# here we do some preparation

tshark = subprocess.Popen([TSHARK_PATH, "-i", INTERFACE_NO, "-b", "filesize:"+OUTPUT_FILE_SIZE_LIMIT, "-f", PACKET_FILTER, "-w", OUTPUT_DIR+OUTPUT_FILE_NAME])

# packet capturing is running, we do some other actions

tshark.terminate()

# do some post processing using captured files

Here is the problem:

  • If I do the same from console (manually) everything is fine -> but the Tshark is stopped by ctrl-c

  • If I do this from Python script (automatically) I get an "partial packet" error -> the Tshark is stopped by terminate()

Is there a way to send "ctrl-c" to the process using Subprocess? I know that Popen can be run with stdin=subprocess.PIPE, but simple write() won't do the trick.

Or maybe I'm doing this backwards and this is not the right solution to do this? I'm using Tshark because it's easier to use this tool for all the heavy lifting.

Windows seem to be rather unfriendly towards Python automation of command line tools.

Top answer
1 of 4
4

This works:

import subprocess
import time
import win32api
import win32con

proc = subprocess.Popen("ping -t localhost", stdin=subprocess.PIPE)
time.sleep(3) # just so it runs for a while

print "sending ctrl c"
try:
	win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, 0)
	proc.wait()
except KeyboardInterrupt:
	print "ignoring ctrl c"

print "still running"

It sends ctrl-c to all processes that share the console of the calling process but then ignores it in the python process with an exception handler. You can't target the subprocess directly because that signal cannot be generated for process groups.

ctypes version

import subprocess
import time
import ctypes
proc = subprocess.Popen("ping -t localhost", stdin=subprocess.PIPE)
time.sleep(3) # just so it runs for a while

try:
	ctypes.windll.kernel32.GenerateConsoleCtrlEvent(0, 0)
	proc.wait()
except KeyboardInterrupt:
	print "ignoring ctrlc"
print "still running"

ctrl-break version

This can target the process specifically but with ping ctrl-break doesn't actually cause it to terminate. You may be able to use it with tshark though.

import subprocess
import time
import ctypes
proc = subprocess.Popen("ping -t localhost", stdin=subprocess.PIPE,
	creationflags=512) # CREATE_NEW_PROCESS_GROUP = 512
time.sleep(3) # just so it runs for a while

ctypes.windll.kernel32.GenerateConsoleCtrlEvent(1, proc.pid) # CTRL_BREAK_EVENT = 1
proc.wait()
2 of 4
2

On Unix, the terminate() functions sends SIGINT, which allows the process to clean up after itself before exiting, while the kill() method sends SIGKILL which ends it immediately. However on Windows both methods call the Windows API function TerminateProcess which straight up kills the process and does not allow cleanup, so tshark never gets a chance to write its files. See here and here.

I'm not much of a Windows expert, but have you tried os.kill(process.pid, signal.CTRL_C_EVENT)?

EDIT: Looking at the documentation for subprocess, it also looks like you need to create the process with the CREATE_NEW_PROCESS_GROUP flag to allow you to send signals to it. As an alternative to os.kill, there is also the slightly cleaner tshark.send_signal(signal.CTRL_C_EVENT).

🌐
Quora
quora.com › How-does-Python-handle-Ctrl-C
How does Python handle Ctrl C? - Quora
If the script is in the background, ... control-C would send by doing ... SIGINT is the signal that is generated when you hit control-C on your keyboard in most Unix or Linux environments. ... Why my windows ctrl+C, ctrl+V shortcut ...
🌐
Xanthium
xanthium.in › operating-system-signal-handling-in-python3
Capturing and Handling OS signals like SIGINT (CTRL-C) in Python | xanthium enterprises
October 18, 2023 - import signal # Import signal module ... signal.signal(signal.SIGINT,SignalHandler_SIGINT) #infinite signal from which we have to escape while 1: print("Press Ctrl + C ") time.sleep(1) ... When the Signal SIGINT is generated this function is called and the print() statement under ...
🌐
Python.org
discuss.python.org › python help
Making Python Script Exit Completely with One Ctrl+C - Python Help - Discussions on Python.org
December 7, 2023 - Dear Python community, I hope this message finds you in good health. I am reaching out to seek your assistance regarding an issue that I have been experiencing with one of my Python scripts that involves the use of libc…
🌐
Narkive
comp.lang.python.narkive.com › 6rdHQvDd › sending-ctrl-c-to-a-process
sending ctrl C to a process
... ^C Hey, why did you press Ctrl-C?! ... import sys ... while 1: ... pass ... except KeyboardInterrupt: ... sys.exit(0) ... ^C **** Python terminates! $ HTH, ... Permalink hi thanks for the reply.Actually, i should clarify what i am doing. the program's output will be displayed to a web browser using cgi, so there is no keyboard interrupt. The backend cgi script (python script) will have to send Ctrl-C to break the program.
Find elsewhere
🌐
Raspberry Pi Forums
forums.raspberrypi.com › board index › community › general discussion
Sending CRTL+C to close shell script - Raspberry Pi Forums
Since you say your program is Python this is how to do it from Python :- ... import subprocess, signal, time print("Starting...") proc = subprocess.Popen("./wait") ## Run program print("Waiting...") time.sleep(2) ## Wait a bit print("Sending CTRL-C...") proc.send_signal(signal.SIGINT) ## Send interrupt signal ## You can also use one of these two #proc.terminate() ## send terminate signal #proc.kill() ## send kill signal (forcibly kill the process, it cannot be trapped so the process can't exit gracefully, use with caution)
🌐
JetBrains
youtrack.jetbrains.com › issue › PY-4840
Send signals (e.g. by CTRL-C) to an opened console
January 22, 2021 - {{ (>_<) }} This version of your browser is not supported. Try upgrading to the latest stable version. Something went seriously wrong
🌐
CopyProgramming
copyprogramming.com › howto › simulate-ctrl-c-to-python-script
Python: Python Script: Emulating Ctrl-C Functionality
April 11, 2023 - Python script simulation of Ctrl-C command, Sending CTRL+C to SSH shell using Python's Paramiko library, Transferring Code from Python to Bash using Ctrl-C, Intercepting Ctrl + C Command in Python Running on Cygwin: A Guide
🌐
GitHub
github.com › microsoft › debugpy › issues › 500
Ctrl-C doesn't send SIGINT to python program launched with debugger · Issue #500 · microsoft/debugpy
December 14, 2020 - In a nutshell, "Ctrl-C" should raise a KeyboardInterupt in a running python program. When run in a vscode-python terminal with python3 program.py, it works as expected.
Author   alexwhittemore
Top answer
1 of 10
20

There is a solution by using a wrapper (as described in the link Vinay provided) which is started in a new console window with the Windows start command.

Code of the wrapper:

#wrapper.py
import subprocess, time, signal, sys, os

def signal_handler(signal, frame):
  time.sleep(1)
  print 'Ctrl+C received in wrapper.py'

signal.signal(signal.SIGINT, signal_handler)
print "wrapper.py started"
subprocess.Popen("python demo.py")
time.sleep(3) #Replace with your IPC code here, which waits on a fire CTRL-C request
os.kill(signal.CTRL_C_EVENT, 0)

Code of the program catching CTRL-C:

#demo.py

import signal, sys, time

def signal_handler(signal, frame):
  print 'Ctrl+C received in demo.py'
  time.sleep(1)
  sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)
print 'demo.py started'
#signal.pause() # does not work under Windows
while(True):
  time.sleep(1)

Launch the wrapper like e.g.:

PythonPrompt> import subprocess
PythonPrompt> subprocess.Popen("start python wrapper.py", shell=True)

You need to add some IPC code which allows you to control the wrapper firing the os.kill(signal.CTRL_C_EVENT, 0) command. I used sockets for this purpose in my application.

Explanation:

Preinformation

  • send_signal(CTRL_C_EVENT) does not work because CTRL_C_EVENT is only for os.kill. [REF1]
  • os.kill(CTRL_C_EVENT) sends the signal to all processes running in the current cmd window [REF2]
  • Popen(..., creationflags=CREATE_NEW_PROCESS_GROUP) does not work because CTRL_C_EVENT is ignored for process groups. [REF2] This is a bug in the python documentation [REF3]

Implemented solution

  1. Let your program run in a different cmd window with the Windows shell command start.
  2. Add a CTRL-C request wrapper between your control application and the application which should get the CTRL-C signal. The wrapper will run in the same cmd window as the application which should get the CTRL-C signal.
  3. The wrapper will shutdown itself and the program which should get the CTRL-C signal by sending all processes in the cmd window the CTRL_C_EVENT.
  4. The control program should be able to request the wrapper to send the CTRL-C signal. This might be implemnted trough IPC means, e.g. sockets.

Helpful posts were:

I had to remove the http in front of the links because I'm a new user and are not allowed to post more than two links.

  • http://social.msdn.microsoft.com/Forums/en-US/windowsgeneraldevelopmentissues/thread/dc9586ab-1ee8-41aa-a775-cf4828ac1239/#6589714f-12a7-447e-b214-27372f31ca11
  • Can I send a ctrl-C (SIGINT) to an application on Windows?
  • Sending SIGINT to a subprocess of python
  • http://bugs.python.org/issue9524
  • http://ss64.com/nt/start.html
  • http://objectmix.com/python/387639-sending-cntrl-c.html#post1443948

Update: IPC based CTRL-C Wrapper

Here you can find a selfwritten python module providing a CTRL-C wrapping including a socket based IPC. The syntax is quite similiar to the subprocess module.

Usage:

>>> import winctrlc
>>> p1 = winctrlc.Popen("python demo.py")
>>> p2 = winctrlc.Popen("python demo.py")
>>> p3 = winctrlc.Popen("python demo.py")
>>> p2.send_ctrl_c()
>>> p1.send_ctrl_c()
>>> p3.send_ctrl_c()

Code

import socket
import subprocess
import time
import random
import signal, os, sys


class Popen:
  _port = random.randint(10000, 50000)
  _connection = ''

  def _start_ctrl_c_wrapper(self, cmd):
    cmd_str = "start \"\" python winctrlc.py "+"\""+cmd+"\""+" "+str(self._port)
    subprocess.Popen(cmd_str, shell=True)

  def _create_connection(self):
    self._connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    self._connection.connect(('localhost', self._port))

  def send_ctrl_c(self):
    self._connection.send(Wrapper.TERMINATION_REQ)
    self._connection.close()

  def __init__(self, cmd):
    self._start_ctrl_c_wrapper(cmd)
    self._create_connection()


class Wrapper:
  TERMINATION_REQ = "Terminate with CTRL-C"

  def _create_connection(self, port):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('localhost', port))
    s.listen(1)
    conn, addr = s.accept()
    return conn

  def _wait_on_ctrl_c_request(self, conn):
    while True:
      data = conn.recv(1024)
      if data == self.TERMINATION_REQ:
        ctrl_c_received = True
        break
      else:
        ctrl_c_received = False
    return ctrl_c_received

  def _cleanup_and_fire_ctrl_c(self, conn):
    conn.close()
    os.kill(signal.CTRL_C_EVENT, 0)

  def _signal_handler(self, signal, frame):
    time.sleep(1)
    sys.exit(0)

  def __init__(self, cmd, port):
    signal.signal(signal.SIGINT, self._signal_handler)
    subprocess.Popen(cmd)
    conn = self._create_connection(port)
    ctrl_c_req_received = self._wait_on_ctrl_c_request(conn)
    if ctrl_c_req_received:
      self._cleanup_and_fire_ctrl_c(conn)
    else:
      sys.exit(0)


if __name__ == "__main__":
  command_string = sys.argv[1]
  port_no = int(sys.argv[2])
  Wrapper(command_string, port_no)
2 of 10
16

New answer:

When you create the process, use the flag CREATE_NEW_PROCESS_GROUP. And then you can send CTRL_BREAK to the child process. The default behavior is the same as CTRL_C, except that it won't affect the calling process.


Old answer:

My solution also involves a wrapper script, but it does not need IPC, so it is far simpler to use.

The wrapper script first detaches itself from any existing console, then attach to the target console, then files the Ctrl-C event.

import ctypes
import sys

kernel = ctypes.windll.kernel32

pid = int(sys.argv[1])
kernel.FreeConsole()
kernel.AttachConsole(pid)
kernel.SetConsoleCtrlHandler(None, 1)
kernel.GenerateConsoleCtrlEvent(0, 0)
sys.exit(0)

The initial process must be launched in a separate console so that the Ctrl-C event will not leak. Example

p = subprocess.Popen(['some_command'], creationflags=subprocess.CREATE_NEW_CONSOLE)

# Do something else

subprocess.check_call([sys.executable, 'ctrl_c.py', str(p.pid)]) # Send Ctrl-C

where I named the wrapper script as ctrl_c.py.

🌐
CopyProgramming
copyprogramming.com › howto › what-is-the-best-way-to-generate-ctrl-c-event-in-python
Python: Python: Generating 'Ctrl+c' Event - What's the Optimal Approach?
March 29, 2023 - Python: Catch Ctrl-C command. Prompt "really want to quit (y/n, #!/usr/bin/env python import ; import sys def ; signal_handler(signal, frame): ; # your code here sys.exit(0) ... As I run a python script on Secure CRT to gather specific data logs, I am unable to stop the logs by pressing ctrl+c on my keyboard.
🌐
PyPI
pypi.org › project › console-ctrl
console-ctrl · PyPI
February 20, 2021 - import console_ctrl import subprocess # Start some command IN A SEPARATE CONSOLE p = subprocess.Popen(['some_command'], creationflags=subprocess.CREATE_NEW_CONSOLE) # Do something else console_ctrl.send_ctrl_c(p.pid)
      » pip install console-ctrl
    
Published   Feb 21, 2021
Version   0.1.0
🌐
Python.org
discuss.python.org › python help
How to create a custom KeyboardInterrupt? - Python Help - Discussions on Python.org
October 10, 2022 - I want to create something very similar to the KeyboardInterrupt exception which is invoked after you press Ctrl-C. However, instead of closing the whole program, my exception will be used internally by my code to end the current task. I want it to be invoked via a custom shortcut (I googled ...