如何使用 Python 多次与命令行交互而不丢失实例特定变量(如工作目录)?

How can I use Python to interact with a command line multiple times without losing instance specific variables like working directory?

我正在尝试在我的程序中模拟命令提示符(例如 VS Code 底部的命令提示符)。

这是我尝试过的:

import os

while(True):
    console_command = input()
    console_stream = os.popen(console_command)
    console_output = console_stream.read()
    print(console_output)

这几乎可以工作,但是当前目录和其他 运行时间特定数据丢失,因为 os.popen 每次调用命令时都会打开一个新的终端实例。最重要的是,使用此实现时,无法使用 cd 命令更改目录。

我还考虑过存储每次用户调用更改目录的命令并将这些调用附加到用户 运行s 的任何命令的前面,但这似乎是个坏主意,因为它可能导致如果用户修改目录,则会出错。这也会使我的代码变得非常混乱,并且会占用大量内存。

注意 - 这不是询问如何在 python 中 运行 一个命令的其他问题的重复,因为我需要在同一个中 运行 多个命令终端并提供实时响应。不能将多个命令组合成一行。

您可以像 shell 那样做,定义一些在本地执行的命令。一种技巧是在它们前面加上“!”以区别于应该通过的命令。

import os

while(True):
    console_command = input()
    if console_command.startswith("!"):
        # do your parsing here. Here is a lame one
        scmd = console_command[1:].split()
        try:
            if scmd[0] == "cd":
                os.chdir(scmd[1])
            else:
                raise IndexError()
        except IndexError:
            print("invalid command")
        continue
       
    console_stream = os.popen(console_command)
    console_output = console_stream.read()
    print(console_output)

解决方案 1 - 使用 subprocess

#!/usr/bin/env python3
import subprocess
import select
import os

shell = subprocess.Popen(["bash"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
poll = select.poll()  # To peek if there's stdout.
poll.register(shell.stdout, select.POLLIN)

while True:
    console_command = input('> ') + '\n'
    shell.stdin.write(str.encode(console_command))
    shell.stdin.flush()

    console_output = b''
    while True:
        has_output = poll.poll(50)  # If no new output within 50ms, we assume all the output is finished.
        if not has_output:
            break
        console_output += os.read(shell.stdout.fileno(), 4096)
    print(console_output.decode())

解决方案 1 的演示

> SOME_STRING=hello_Whosebug

> echo $SOME_STRING
hello_Whosebug

> pwd
/tmp

> cd ~/Documents

> pwd     
/Users/my_user_name/Documents

解决方案 2 - 使用 pexpect

对于此方法,您将与真实的 shell 进行交互。就像VS Code底部的那个一样。

import pexpect

shell = pexpect.spawn('sh')
shell.interact()