-1

I have a CLI binary that I would like to control through Python. How could this easily be done?

I have tried making a terminal in a subprocess and opening it that way but that didn't work. I also thought about using keyboard emulation but that would be unreliable imo.

More specifically, I want to use Stockfish's CLI through Python.

The "I have tried making a terminal in a subprocess[...]" part refers to me trying to do that but being too unknowledgeable to actually achieve something.

minimal reproducible example:

import subprocess

My_Cmmnd = "cd"

process = subprocess.Popen(
    "powershell -e 'bash -c \"" + My_Cmmnd + ";bash\"'",
    stdout=subprocess.PIPE,
    stderr=None,
    shell=True,
)

As you can probably tell, this doesn't work.

I've also tried:

import os
    
os.system("cd path/to/stockfish")
os.system("cd")

This returns C:\path\to\project and not C:\path\to\stockfish as I would've expected.

I am open to any method, and am not saying I have to do this this specific way.

It was proposed that this may be similar to my question. In essence it is, but

  • its outdated

  • has no verified solution

  • when i try to use cd, it cannot change directory as it cannot find it for some reason

import subprocess

command = subprocess.Popen(['cd C:\\path\\to\\stockfish'], shell=True)

output: The filename, directory name, or volume label syntax is incorrect.

I pasted it into cmd and it worked just fine.

It was pointed out to me that there seems to be no cli, and i only use cd in the example. That is true in, the example i only use cd, but thats just for an example, if cd works and it correctly changes the directory than the whole thing works as i can then launch the cli and send commands to it.

7
  • 4
    Why do you start a new process for cd C:\\path\\to\\stockfish? Does this answer your question? How do I change the working directory in Python? TL;DR os.chdir("path/to/stockfish"). The title mentions a CLI but it looks like you only want to change the current working directory. Commented Jul 16 at 8:38
  • You are on Windows, why don't you use the Windows executable instead of calling bash on the Linux executable? This would remove one layer of complexity and allow you to debug it more easily Commented Jul 16 at 8:43
  • 2
    There is a big difference between cd and a program. cd is a shell built-in command. You don't start it with subprocess.Popen. You change the current working directory with os.chdir or contextlib.chdir. It makes no sense to use cd as an example for a Stockfish CLI. Why do you think, you need to change the current working directory? Commented Jul 16 at 13:48
  • @globglogabgalab Why did you add the Powershell tag? Why do you need a shell at all for this question? Removing Powershell "would remove one layer of complexity". Why did you add the Windows tag? Changing the current working directory works the same for Windows and Linux in Python. Commented Jul 16 at 13:51
  • 1
    do you really have to change folder? Can't you run directly .exe with /full/path/stockfish/program.exe? And if you need to change folder then you have to do all in one process - for example os.system("cd path/to/stockfish ; program.exe") Commented Jul 16 at 14:54

1 Answer 1

1

TLDR; if you want to try to fix it yourself without looking at code:

1. Use subprocess.Popen with pipes for interactive terminal programs.

2. Specifically for stockfish, consider using python-chess.

3. subprocesses don't just interact with each other, so running cd in a new subprocess won't work because the working directory of the new process will change and it will immediately exit. this directory changing only affects the child process that was created, not the parent.

Long answer:

Use subprocess with pipes for stdin/stdout communication.

import subprocess

# Start the Stockfish process.
stockfish = subprocess.Popen(
    ['path/to/stockfish'],
    universal_newlines=True, # for text mode (Python 3.7+ can use text=True)
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE
)

def send_command(command):
    """send a command to stockfish and return the output."""
    stockfish.stdin.write(command + '\n') # stockfish requires \n at the end of each command.
    stockfish.stdin.flush()

    output = []
    while True:
        line = stockfish.stdout.readline().strip()
        if line == '': # Empty string means process has ended.
            break
        if line == 'readyok': # Common stockfish response marker
            break
        output.append(line)
    return '\n'.join(output)

# Example usage:
print(send_command('uci'))
print(send_command('isready'))
print(send_command('quit'))

remember that stockfish doesn't always respond with readyok, but I provided you with the template and you can add more cases yourself. if you feel like adding cases manually is tedious, read a later section where I talk about using python-chess. using python-chess is recommended unless you need to use raw subprocess.

read this documentation from docs.python.org about subprocesses and how to handle them.

Why cd Didn't work:

when you run subprocess.Popen(['cd', 'path']), it creates a new separate process that changes directory and then immediately exits, so the process you want doesn't change directory. The directory only changes inside the child process you created, not the parent, or the stockfish process.

Specifically for stockfish, you can use python-chess, which has built-in support for stockfish:

import chess.engine

engine = chess.engine.SimpleEngine.popen_uci("path/to/stockfish")

board = chess.Board()
result = engine.play(board, chess.engine.Limit(time=0.1))
print(result.move)

engine.quit()

read this documentation from python-chess.readthedocs.io to learn how to use this library.

If you HAVE to change directory before running stockfish:

import subprocess
import os

stockfish_path = "path/to/stockfish"
os.chdir(stockfish_path)  # Change Python process's working directory. this changes the directory of the actual running python.

# Now start Stockfish
stockfish = subprocess.Popen(
    ['./stockfish'],  # './' is important on Unix-like systems, ./stockfish.exe for Windows. you also might need to set shell=True on Windows.
    universal_newlines=True,
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE
)

The code blocks I presented above are rough solutions, and you might need to adjust some stuff to make it work with your setup.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.