Windows: подпроцесс, создающий новое окно консоли, теряющий стандартный вывод - PullRequest
3 голосов
/ 13 ноября 2011

Я использую Windows Vista и Python 2.7.2, но ответы не обязательно должны быть на Python.

Так что я могу нормально запускать и взаимодействовать с подпроцессами stdin / stdout (используя python), для программ командной строки, таких как `dir '.
- тем не мение -
Программа, которую я теперь хочу называть, любит создавать для себя новое окно консоли для Windows (не curses), с новыми дескрипторами, даже когда запускается из уже существующего окна cmd.exe. (Странно, поскольку это интерфейс «дистанционного управления» VLC.) Есть ли способ:

  1. получение дескрипторов для стандартного ввода / вывода консоли; или
  2. заставить новую оболочку работать внутри старой (например, вызвать bash изнутри bash)?

Если это не удастся, чтобы я мог взломать код подпроцесса, как настроить новую консоль в Windows и перенести вход / выход?

Edit: * 1015 Т.е. *

>>> p = Popen(args=['vlc','-I','rc'],stdin=PIPE,stdout=PIPE)
# [New console appears with text, asking for commands]
>>> p.stdin.write("quit\r\n")
Traceback:
    File "<stdin>", line 1, in <module>
IOError: [Errno 22] Invalid argument
>>> p.stdout.readline()
''
>>> p.stdout.readline()
''
# [...]

Но новое окно консоли, которое появляется, также не принимает ввод с клавиатуры.

В то время как обычно:

>>> p = Popen(args=['cmd'],stdin=PIPE,stdout=PIPE)
>>> p.stdin.write("dir\r\n")
>>> p.stdin.flush()
>>> p.stdout.readline() #Don't just do this IRL, may block.
'Microsoft Windows [Version...

Ответы [ 2 ]

1 голос
/ 13 ноября 2011

Я не получил интерфейс rc для работы с piped stdin / stdout в Windows;Я получаю IOError при всех попытках communicate или пишу напрямую stdin.Есть опция --rc-fake-tty, которая позволяет создавать интерфейс rc в Linux, но она недоступна в Windows - по крайней мере, в моей несколько устаревшей версии VLC (1.1.4).С другой стороны, использование интерфейса сокетов работает нормально.

Структура, назначенная параметру startupinfo и используемая функцией Win32 CreateProcess, может быть настроена для скрытия окна процесса.Однако для консоли VLC rc я думаю, что проще использовать существующую опцию --rc-quiet.В общем, вот как настроить startupinfo, чтобы скрыть окно процесса:

startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.Popen(cmd, startupinfo=startupinfo)

Просто для завершения - на случай, если использование каналов также не работает в вашей системе - вот небольшая демонстрация, которую я приготовилиспользуя опцию --rc-host для связи через сокет.Он также использует --rc-quiet, чтобы скрыть консоль.Это просто печатает помощь и выходит.Я больше ничего не проверял.Я проверил, что он работает в Python версий 2.7.2 и 3.2.2.(Я знаю, что вы не просили об этом, но, тем не менее, это может быть полезно для вас.)

import socket
import subprocess
from select import select

try:
    import winreg
except ImportError:
    import _winreg as winreg

def _get_vlc_path():
    views = [(winreg.HKEY_CURRENT_USER, 0),
             (winreg.HKEY_LOCAL_MACHINE, winreg.KEY_WOW64_64KEY),
             (winreg.HKEY_LOCAL_MACHINE, winreg.KEY_WOW64_32KEY)]
    subkey = r'Software\VideoLAN\VLC'
    access = winreg.KEY_QUERY_VALUE
    for hroot, flag in views:
        try:
            with winreg.OpenKey(hroot, subkey, 0, access | flag) as hkey:
                value, type_id = winreg.QueryValueEx(hkey, None)
                if type_id == winreg.REG_SZ:
                    return value
        except WindowsError:
            pass
    raise SystemExit("Error: VLC not found.")

g_vlc_path = _get_vlc_path()

def send_command(sock, cmd, get_result=False):
    try:
        cmd = (cmd + '\n').encode('ascii')
    except AttributeError:
        cmd += b'\n'
    sent = total = sock.send(cmd)
    while total < len(cmd):
        sent = sock.send(cmd[total:])
        if sent == 0:
            raise socket.error('Socket connection broken.')
        total += sent
    if get_result:
        return receive_result(sock)

def receive_result(sock):
    data = bytearray()
    sock.setblocking(0)
    while select([sock], [], [], 1.0)[0]:
        chunk = sock.recv(1024)
        if chunk == b'': 
            raise socket.error('Socket connection broken.')
        data.extend(chunk)
    sock.setblocking(1)
    return data.decode('utf-8')

def main(address, port):
    import time
    rc_host = '{0}:{1}'.format(address, port)
    vlc = subprocess.Popen([g_vlc_path, '-I', 'rc', '--rc-host', rc_host, 
                            '--rc-quiet'])
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        sock.connect((address, port))
        help_msg = send_command(sock, 'help', True)
        print(help_msg)
        send_command(sock, 'quit')
    except socket.error as e:
        exit("Error: " + e.args[0])
    finally:
        sock.close()
        time.sleep(0.5)
        if vlc.poll() is None:
            vlc.terminate()

if __name__ == '__main__':
    main('localhost', 12345)
0 голосов
/ 07 июля 2013

Применительно к мониторингу stdOut, который появляется в новом окне консоли.

Здесь другой вопрос / ответ , который решает проблему.

В итоге(в ответе Adam MW ):

  • Подавить новую порожденную консоль, запустив vlc в тихом режиме --intf=dummy --dummy-quiet или --intf=rc --rc-quiet.
  • Monitor stdErr запущенного процесса

Примечание. Что касается команд stdIn для интерфейса rc, решение --rc-host описывается ответом eryksun

...