TCL получает команду с опцией -nohang? - PullRequest
2 голосов
/ 04 января 2012

Вот код, который просто реализует интерактивный сеанс TCL с командной строкой MyShell >.

puts -nonewline stdout "MyShell > "
flush stdout
catch { eval [gets stdin] } got
if { $got ne "" } {
    puts stderr $got
}

Этот код запрашивает MyShell > на терминале и ожидает нажатия кнопки ввода; пока он не попал в код ничего не делает. Это то, что делает команда gets.

Что мне нужно, так это некоторая альтернатива команде gets, скажем coolget. Команда coolget должна не ждать кнопку ввода, а зарегистрировать некоторый слот, который будет вызываться при нажатии, и просто продолжить выполнение. Нужный код должен выглядеть так:

proc evaluate { string } \
{
    catch { eval $string } got
    if { $got ne "" } {
        puts stderr $got
    }
}

puts -nonewline stdout "MyShell > "
flush stdout
coolgets stdin evaluate; # this command should not wait for the enter button
# here goes some code which is to be executed before the enter button is hit

Вот что мне было нужно:

proc prompt { } \
{
   puts -nonewline stdout "MyShell > "
   flush stdout
}


proc process { } \
{
   catch { uplevel #0 [gets stdin] } got
   if { $got ne "" } {
       puts stderr $got
       flush stderr
   }
   prompt
}

fileevent stdin readable process

prompt
while { true } { update; after 100 }

Ответы [ 2 ]

4 голосов
/ 04 января 2012

Tcl применяет функциональность, подобную «nohang», ко всему каналу, и это делается путем настройки неблокирующего канала.После этого любой read вернет только те данные, которые есть, gets вернет только полные строки, которые доступны без ожидания, а puts (на записываемом канале) организует отправку своего вывода наОС асинхронно. Это зависит от работоспособности цикла событий.

Рекомендуется использовать неблокирующие каналы с зарегистрированным обработчиком событий файла.Вы можете объединить это с неблокированием, чтобы реализовать вашу coolget идею:

proc coolget {channel callback} {
    fileevent $channel readable [list apply {{ch cb} {
        if {[gets $ch line] >= 0} {
            uplevel [lappend cb $line]
        } elseif {[eof $ch]} {
            # Remove handler at EOF: important!
            fileevent $ch readable {}
        }
    }} $channel $callback]
}

Это будет работать просто отлично, за исключением того, что вам придется вызывать либо vwait, либо update для обработкисобытия (если вы не используете Tk тоже; Tk особенный), поскольку Tcl не будет обрабатывать вещи магически в фоновом режиме;Волшебная фоновая обработка вызывает больше проблем, чем стоит…


Если вы сильно запутались в асинхронной обработке событий, рассмотрите возможность использования сопрограмм Tcl 8.6 для реструктуризации кода.В частности, такой код как Coronet может сильно помочь.Однако это очень сильно зависит от Tcl 8.6, так как более ранние реализации Tcl вообще не поддерживают сопрограммы;низкоуровневая реализация должна была быть переписана с простых C-вызовов на продолжения, чтобы включить эти функции, а это не позволяет осуществлять бэкпорт при разумных усилиях.

4 голосов
/ 04 января 2012

Я думаю, вам нужно взглянуть на команды fileevent, fconfigure и vwait. Используя их, вы можете сделать что-то вроде следующего:

proc GetData {chan} {
    if {[gets $chan line] >= 0} {
       puts -nonewline "Read data: "
       puts $line
    }
}

fconfigure stdin -blocking 0 -buffering line -translation crlf
fileevent stdin readable [list GetData stdin]

vwait x

Этот код регистрирует GetData как читаемый обработчик событий файла для stdin, поэтому, когда есть данные, доступные для чтения, он вызывается.

...