Djent
Djent

Reputation: 3467

Killing subprocess after first line

I am executing program which connects to external server from python.
If user is not authenticated, the program asks for username and password.

Here is how subprogram output looks:

Authentication Required

Enter authorization information for "Web API"

<username_prompt_here>
<password_prompt_here>

I want to kill subprocess right after 'Authentication Required' is printed, but the problem is, that my code works wrong - subprocess is asking for credentials and after user provides it, the subprocess is killed.

Here is my code:

with subprocess.Popen(self.command, stdout=subprocess.PIPE, shell=True, bufsize=1, universal_newlines=True) as process:
    for line in process.stdout:
        if 'Authentication Required' in line:
            print('No authentication')
            process.kill()
        print(line)

What am I doing wrong?

Upvotes: 1

Views: 656

Answers (1)

jfs
jfs

Reputation: 414255

What am I doing wrong?

Your code is ok (if you want to kill the subprocess after 'Authentication Required' line regardless its position) if the child process flushes its stdout buffer in time. See Python: read streaming input from subprocess.communicate()

The observed behavior indicates that the child uses a block-buffering mode and therefore your parent script sees the 'Authentication Required' line too late or that killing the shell with process.kill() doesn't kill its descendants (processes created by the command).

To workaround it:

  • See whether you could pass a command-line argument such as --line-buffered (accepted by grep), to force a line-buffered mode
  • Or see whether stdbuf, unbuffer, script utilities work in your case
  • Or provide a pseudo-tty to hoodwink the process into thinking that it runs in a terminal directly — it may also force the line-buffered mode.

See code examples in:


And - not always I want to kill program after first line. Only if first line is 'Authentication required'

Assuming the block-buffering issue is fixed, to kill the child process if the first line contains Authentication Required:

with Popen(shlex.split(command), 
           stdout=PIPE, bufsize=1, universal_newlines=True) as process:
    first_line = next(process.stdout)
    if 'Authentication Required' in first_line:
        process.kill()
    else: # whatever
        print(first_line, end='')
        for line in process.stdout:
            print(line, end='')

If shell=True is necessary in your case then see How to terminate a python subprocess launched with shell=True.

Upvotes: 2

Related Questions