Skip to content

shell command does not terminate on cancel #3237

Open
@zoranbosnjak

Description

@zoranbosnjak

As a simplified example, I would like to:

  • start a shell command
  • wait some time
  • stop it

The problem is that a child subprocesses from the shell won't terminate on cancel and would keep running even after python process is finished.

The problem is present only when a command consists of several processes, like ping localhost | cat, but not when a command is simple, like sleep 20.

Tested with: ubuntu-24.04, python 3.12.8, trio 0.29.0

Steps to reproduce:

  • check for currently running ping processes: ps aux | grep ping (confirm no ping process is running)
  • run the test script (see below) python ./test.py
  • check list of processes again, while the test script is running and after the test script terminates

Expected result is that a shell + ping + cat are all running only for 5 seconds.

Actual result that a shell process is running for 5 seconds, but the ping and also cat processes remain running not just beyond 5 seconds, but even after a script terminates. It looks like the subprocesses get detached from the shell and are not considered childs any more. I am not sure if this is 1 or 2 separate problems. I am not even sure if this is a proper way to run a shell script from within trio.

In any case, is there a workaround or a better way to run shell commands?

test.py script:

import trio
from subprocess import DEVNULL

# cmd = 'sleep 20'  # this is properly stopped
cmd = 'ping localhost | cat'  # this command won't stop properly

async def test():
    async with trio.open_nursery() as ns:
        async def service_runner(task_status = trio.TASK_STATUS_IGNORED):
            with trio.CancelScope() as scope:
                task_status.started(scope)
                await trio.run_process(cmd, shell=True, check=False,
                                        stdout=DEVNULL, stderr=DEVNULL)
                ns.cancel_scope.cancel()

        service_scope = await ns.start(service_runner)
        await trio.sleep(5.0)
        service_scope.cancel()
        await trio.sleep(5.0)

trio.run(test)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions