Nightscape
Nightscape

Reputation: 464

Interact with the Windows CMD in python

I am trying to open a windows CMD and read/write into it. I managed to open the CMD with this command:

import subprocess
proc = subprocess.Popen('cmd.exe')

To write into the CMD console, I tried the solution from this question:

proc.communicate('cd Documents')

This line automatically closed the CMD, so i wasn't able to see if it worked.

How can I read and write into the Windows CMD?

Upvotes: 1

Views: 5426

Answers (2)

Ioannis Tsampras
Ioannis Tsampras

Reputation: 46

This might be of help ! Simple CMD Python Interface for executing sequential commands:

import subprocess
class CmdInterface:
    def __init__(self,nude=True,rm_boilerplate=True,end_signal="cmd_end_command_signal",log_mode=False):
        
        self.end_command_signal=end_signal
        self.nude=nude
        self.process = subprocess.Popen("cmd", stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True, bufsize=1)
        self.lock=False
        self.command_count=0
        self.log_mode=log_mode
        self.log=[]



        if rm_boilerplate==True:
            #just an empty first command to clear the boiler plate for the next ones, this is not added in the command count #TODO MAKE SOMETHING THAT DOESNT INJECT A NEW COMMAND
            self.send_command("@echo.")


    def __del__(self):
        # Clean up
        self.process.stdin.close()
        self.process.terminate()
        self.process.wait()

    def kill(self):
        # Clean up
        self.process.stdin.close()
        self.process.terminate()
        self.process.wait()

    def send_command(self,command):
        
        #multithreading prevention
        while True:
            if self.lock==False:
                break
        self.lock=True
        self.command_count+=1

        
        # Define a unique signal to indicate the end of a command's output
        end_command_signal = self.end_command_signal
        extension=f"&& echo {end_command_signal}"
        command_with_signal = f"{command} {extension}\n"
        self.process.stdin.write(command_with_signal)
        self.process.stdin.flush()

        # Read output until the end signal is detected
        output = []
        cnt=0
        while True:
            line = self.process.stdout.readline()
            if end_command_signal in line:
                cnt+=1
            if cnt==2:
                break  # Stop reading after detecting the end signal
            if self.nude==True:
                output.append(line.replace(extension,""))
            else:
                output.append(line)
        if self.log_mode==True:
            self.log+=output
        self.lock=False
        return output
    

    def get_log(self):
        return self.log.copy()
    
    def turn_on_logging(self):
        self.log_mode=True

    def turn_off_logging(self):
        self.log_mode=False

    def get_command_count(self):
        return int(self.command_count)

Usage example:

from CmdInterface import CmdInterface 
#     ^^^ replace with where file is located/named

# Initialize 

cmd=CmdInterface()

# Send commands and capture outputs
output_hello_world = cmd.send_command( "echo Hello, World!")
output_dir = cmd.send_command( "dir")
output_dir2 = cmd.send_command( "cd ..")
output_dir3 = cmd.send_command("dir")

# Output results
print("Output of 'Hello, World!':", ''.join(output_hello_world))
print("Output of 'dir':", ''.join(output_dir))
print("Output of 'dir2':", ''.join(output_dir2))
print("Output of 'dir3':", ''.join(output_dir3))

Upvotes: 0

Jean-François Fabre
Jean-François Fabre

Reputation: 140286

communicate sends the contents of the buffer to standard input then closes the input pipe, which ends up terminating the process. So you cannot do something interactive with that.

Moreover, you have to pass stdin argument to Popen or by default nothing is redirected.

import subprocess
proc = subprocess.Popen('cmd.exe',stdin=subprocess.PIPE)

now you can write lines to proc.stdin (don't forget line terminators & binary prefix for python 3 compat.) and see what happens

proc.stdin.write(b"cd Documents\n")

(okay, you could have used cwd="Documents" for that one, but that's for the sake of the example)

In the example, output is not redirected. Which means that you'll see the output in the current console. Don't forget to close standard input or that won't work (probably because buffer isn't flushed and/or pipe is broken when python quits). Then wait for process to finish with wait()

Complete example:

import subprocess
proc = subprocess.Popen('cmd.exe',stdin=subprocess.PIPE)
proc.stdin.write(b"cd ..\n")
# do some stuff in between...
proc.stdin.write(b"cd\n")
proc.stdin.close()
proc.wait()

on my computer it prints (excuse my french)

Microsoft Windows [version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. Tous droits réservés.

L:\so>cd ..

L:\>cd
L:\

if you want the process not to terminate, you could use some more tricks: import subprocess,time

proc = subprocess.Popen('cmd.exe',stdin=subprocess.PIPE)
proc.stdin.write(b"cd ..\n")
proc.stdin.write(b"cd\n")
proc.stdin.flush()
time.sleep(1)
input("press return")
proc.stdin.write(b"cd\n")
proc.stdin.flush()
proc.stdin.close()
proc.wait()

this sends commands, flushes standard input (but doesn't close it) then waits for the messages to print, and asks for a key to be pressed. You can send more commands after that, as long as you flush each time, and close in the end.

Upvotes: 3

Related Questions