超时不到1秒怎么办?

时间:2013-02-19 10:03:31

标签: expect

要编写更健壮的脚本,“忘记”expect缓冲区的内容以确保仅在最近收到的输入上进行匹配是很有用的:

# this leaves expect buffer with unmatched history    
# + accumulates incoming data over 1 sec
set timeout 1
expect

# match everything in the buffer ~"forget"
expect *
# subsequent expect commands will see only what appeared since now

是否可以在不修补预期来源的情况下使超时小于1秒?

注意:set timeout 0将无效,因为第一个期望不会将新传入的数据保留在缓冲区中。

2 个答案:

答案 0 :(得分:0)

我不确定如何在tcl解释器中刷新缓冲区。

我不确定您的用例,但我发现远程shell脚本编写的最可靠格式超出预期,最简单的方法是在每个脚本末尾包含一个#randomnumber发送,期望#randomnumber,这可以确保缓冲区同步到我发送到生成进程的最后一行。如果产生的过程没有回显您发送的字符,您的里程会有所不同。

如果你可以从TCL实现转移到python,那么来自pexpect的纯python实现是很好的。缓冲区的工作方式略有不同,因此需要一些时间来适应。如果您正在通过远程shell执行命令,我建议python-remote(我写的)

您可以通过上面使用的方法加载缓冲区

import pexpect
spawn = pexpect.spawn(command)
stuff_inbuffer = spawn.read_nonblocking(size=100000, timeout=0.1)

在repsonse之前发送随机字符串以同步缓冲区

import random, pexpect
spawn = pexpect.spawn(command)
rand = random.random()
spawn.sendline(command + " #%s" %(rand))
spawn.expect("%s\r\n" %(rand))

然后你可以使用和期望获取缓冲区,或者读取哪个将等到缓冲区大小,或者超时超时。

results = spwan.read(size=100000, timeout=10)

spawn.expect("something")
results = spawn.buffer

results = spawn.before

答案 1 :(得分:-1)

补丁期望很容易...使用负超时毫秒(除了-1,这是特殊的):

# set timeout to 100 milliseconds
set timeout -100

以下命名为milliExpect.patch ... cd到expect5.45目录并执行

patch -Np1 -i milliExpect.patch.

然后通常(可能必须规定tcl在配置中的位置)......

./configure; make; sudo make install

--- milliExpect.patch ----

--- expect5.45_orig/exp_event.c 2010-06-30 17:53:49.000000000 -0700
+++ expect5.45/exp_event.c  2014-09-30 12:50:18.733698995 -0700
@@ -277,6 +277,117 @@
     }
 }

+/* returns status, one of EOF, TIMEOUT, ERROR or DATA */
+/* can now return RECONFIGURE, too */
+/*ARGSUSED*/
+int exp_get_next_event_d(interp,esPtrs,n,esPtrOut,timeout,key)
+Tcl_Interp *interp;
+ExpState *(esPtrs[]);
+int n;         /* # of esPtrs */
+ExpState **esPtrOut;   /* 1st ready esPtr, not set if none */
+double timeout;        /* milliseconds */
+int key;
+{
+    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+    ExpState *esPtr;
+    int i; /* index into in-array */
+#ifdef HAVE_PTYTRAP
+    struct request_info ioctl_info;
+#endif
+
+    int old_configure_count = exp_configure_count;
+
+    int timerFired = FALSE;
+    Tcl_TimerToken timerToken = 0;/* handle to Tcl timehandler descriptor */
+    /* We must delete any timer before returning.  Doing so throughout
+     * the code makes it unreadable; isolate the unreadable nonsense here.
+     */
+#define RETURN(x) { \
+   if (timerToken) Tcl_DeleteTimerHandler(timerToken); \
+   return(x); \
+    }
+
+    for (;;) {
+   /* if anything has been touched by someone else, report that */
+   /* an event has been received */
+
+   for (i=0;i<n;i++) {
+       tsdPtr->rr++;
+       if (tsdPtr->rr >= n) tsdPtr->rr = 0;
+
+       esPtr = esPtrs[tsdPtr->rr];
+
+       if (esPtr->key != key) {
+       esPtr->key = key;
+       esPtr->force_read = FALSE;
+       *esPtrOut = esPtr;
+       RETURN(EXP_DATA_OLD);
+       } else if ((!esPtr->force_read) && (!expSizeZero(esPtr))) {
+       *esPtrOut = esPtr;
+       RETURN(EXP_DATA_OLD);
+       } else if (esPtr->notified) {
+       /* this test of the mask should be redundant but SunOS */
+       /* raises both READABLE and EXCEPTION (for no */
+       /* apparent reason) when selecting on a plain file */
+       if (esPtr->notifiedMask & TCL_READABLE) {
+           *esPtrOut = esPtr;
+           esPtr->notified = FALSE;
+           RETURN(EXP_DATA_NEW);
+       }
+       /*
+        * at this point we know that the event must be TCL_EXCEPTION
+        * indicating either EOF or HP ptytrap.
+        */
+#ifndef HAVE_PTYTRAP
+       RETURN(EXP_EOF);
+#else
+       if (ioctl(esPtr->fdin,TIOCREQCHECK,&ioctl_info) < 0) {
+           expDiagLog("ioctl error on TIOCREQCHECK: %s", Tcl_PosixError(interp));
+           RETURN(EXP_TCLERROR);
+       }
+       if (ioctl_info.request == TIOCCLOSE) {
+           RETURN(EXP_EOF);
+       }
+       if (ioctl(esPtr->fdin, TIOCREQSET, &ioctl_info) < 0) {
+           expDiagLog("ioctl error on TIOCREQSET after ioctl or open on slave: %s", Tcl_ErrnoMsg(errno));
+       }
+       /* presumably, we trapped an open here */
+       /* so simply continue by falling thru */
+#endif /* !HAVE_PTYTRAP */
+       }
+   }
+
+   if (!timerToken) {
+       if (timeout >= 0) {
+       timerToken = Tcl_CreateTimerHandler((int)timeout,
+           exp_timehandler,
+           (ClientData)&timerFired);
+       }
+   }
+
+   /* make sure that all fds that should be armed are */
+   for (i=0;i<n;i++) {
+       esPtr = esPtrs[i];
+       /*printf("CreateChannelHandler: %s\r\n",esPtr->name);*/
+       Tcl_CreateChannelHandler(
+                    esPtr->channel,
+                    TCL_READABLE | TCL_EXCEPTION,
+                    exp_channelhandler,
+                    (ClientData)esPtr);
+       esPtr->fg_armed = TRUE;
+   }
+
+   Tcl_DoOneEvent(0);  /* do any event */
+   
+   if (timerFired) return(EXP_TIMEOUT);
+   
+   if (old_configure_count != exp_configure_count) {
+       RETURN(EXP_RECONFIGURE);
+   }
+    }
+}
+
 /* Having been told there was an event for a specific ExpState, get it */
 /* This returns status, one of EOF, TIMEOUT, ERROR or DATA */
 /*ARGSUSED*/
--- expect5.45_orig/expect.c    2010-10-26 15:09:36.000000000 -0700
+++ expect5.45/expect.c 2014-09-30 13:01:42.693800013 -0700
@@ -41,6 +41,12 @@
 #include "tcldbg.h"
 #endif

+#define TclUtfToUniChar(str, chPtr) \
+   ((((unsigned char) *(str)) < 0xC0) ?        \
+       ((*(chPtr) = (Tcl_UniChar) *(str)), 1)  \
+       : Tcl_UtfToUniChar(str, chPtr))
+
+
 #include "retoglob.c" /* RE 2 GLOB translator C variant */

 /* initial length of strings that we can guarantee patterns can match */
@@ -123,6 +129,7 @@
    int duration;           /* permanent or temporary */
    int timeout_specified_by_flag;  /* if -timeout flag used */
    int timeout;            /* timeout period if flag used */
+   double timeout_double;  /* if timeout < -1 */
    struct exp_cases_descriptor ecd;
    struct exp_i *i_list;
 } exp_cmds[4];
@@ -559,6 +566,11 @@
            goto error;
        }
        eg->timeout_specified_by_flag = TRUE;
+       if (eg->timeout < -1) {
+           eg->timeout_double = (double)eg->timeout * -1.;
+       } else {
+           eg->timeout_double = (double)eg->timeout * 1000.;
+       }
        break;
        case EXP_ARG_NOBRACE:
        /* nobrace does nothing but take up space */
@@ -1812,6 +1824,74 @@
     return cc; 
 }

+/* returns # of bytes read or (non-positive) error of form EXP_XXX */
+/* returns 0 for end of file */
+/* If timeout is non-zero, set an alarm before doing the read, else assume */
+/* the read will complete immediately. */
+/*ARGSUSED*/
+static int
+expIRead_d( /* INTL */
+    Tcl_Interp *interp,
+    ExpState *esPtr,
+    double timeout,
+    int save_flags)
+{
+    int cc = EXP_TIMEOUT;
+    int size;
+
+    /* We drop one third when are at least 2/3 full */
+    /* condition is (size >= max*2/3) <=> (size*3 >= max*2) */
+    if (expSizeGet(esPtr)*3 >= esPtr->input.max*2)
+   exp_buffer_shuffle(interp,esPtr,save_flags,EXPECT_OUT,"expect");
+    size = expSizeGet(esPtr);
+
+#ifdef SIMPLE_EVENT
+ restart:
+
+    alarm_fired = FALSE;
+
+    if (timeout > -1) {
+       if (timeout > 0) {
+           usleep((int)timeout * 1000);
+       } else {
+           usleep(1000 * 1); /* ?? is 1 ms enough ??? */
+       }
+    }
+#endif
+
+    cc = Tcl_ReadChars(esPtr->channel, esPtr->input.newchars,
+              esPtr->input.max - esPtr->input.use,
+              0 /* no append */);
+    i_read_errno = errno;
+
+    if (cc > 0) {
+        memcpy (esPtr->input.buffer + esPtr->input.use,
+       Tcl_GetUnicodeFromObj (esPtr->input.newchars, NULL),
+       cc * sizeof (Tcl_UniChar));
+   esPtr->input.use += cc;
+    }
+
+#ifdef SIMPLE_EVENT
+    alarm(0);
+
+    if (cc == -1) {
+   /* check if alarm went off */
+   if (i_read_errno == EINTR) {
+       if (alarm_fired) {
+       return EXP_TIMEOUT;
+       } else {
+       if (Tcl_AsyncReady()) {
+           int rc = Tcl_AsyncInvoke(interp,TCL_OK);
+           if (rc != TCL_OK) return(exp_tcl2_returnvalue(rc));
+       }
+       goto restart;
+       }
+   }
+    }
+#endif
+    return cc; 
+}
+
 /*
  * expRead() does the logical equivalent of a read() for the expect command.
  * This includes figuring out which descriptor should be read from.
@@ -1932,6 +2012,126 @@
     }
     return(cc);
 }
+/*
+ * expRead_d() does the logical equivalent of a read() for the expect command.
+ * This includes figuring out which descriptor should be read from.
+ *
+ * The result of the read() is left in a spawn_id's buffer rather than
+ * explicitly passing it back.  Note that if someone else has modified a buffer
+ * either before or while this expect is running (i.e., if we or some event has
+ * called Tcl_Eval which did another expect/interact), expRead will also call
+ * this a successful read (for the purposes if needing to pattern match against
+ * it).
+ */
+
+/* if it returns a negative number, it corresponds to a EXP_XXX result */
+/* if it returns a non-negative number, it means there is data */
+/* (0 means nothing new was actually read, but it should be looked at again) */
+int
+expRead_d(
+    Tcl_Interp *interp,
+    ExpState *(esPtrs[]),      /* If 0, then esPtrOut already known and set */
+    int esPtrsMax,         /* number of esPtrs */
+    ExpState **esPtrOut,       /* Out variable to leave new ExpState. */
+    double timeout,
+    int key)
+{
+    ExpState *esPtr;
+
+    int size;
+    int cc;
+    int write_count;
+    int tcl_set_flags; /* if we have to discard chars, this tells */
+           /* whether to show user locally or globally */
+
+    if (esPtrs == 0) {
+   /* we already know the ExpState, just find out what happened */
+   cc = exp_get_next_event_info(interp,*esPtrOut);
+   tcl_set_flags = TCL_GLOBAL_ONLY;
+    } else {
+   cc = exp_get_next_event_d(interp,esPtrs,esPtrsMax,esPtrOut,timeout,key);
+   tcl_set_flags = 0;
+    }
+
+    esPtr = *esPtrOut;
+
+    if (cc == EXP_DATA_NEW) {
+   /* try to read it */
+   cc = expIRead_d(interp,esPtr,timeout,tcl_set_flags);
+   
+   /* the meaning of 0 from i_read means eof.  Muck with it a */
+   /* little, so that from now on it means "no new data arrived */
+   /* but it should be looked at again anyway". */
+   if (cc == 0) {
+       cc = EXP_EOF;
+   } else if (cc > 0) {
+       /* successfully read data */
+   } else {
+       /* failed to read data - some sort of error was encountered such as
+        * an interrupt with that forced an error return
+        */
+   }
+    } else if (cc == EXP_DATA_OLD) {
+   cc = 0;
+    } else if (cc == EXP_RECONFIGURE) {
+   return EXP_RECONFIGURE;
+    }
+
+    if (cc == EXP_ABEOF) { /* abnormal EOF */
+   /* On many systems, ptys produce EIO upon EOF - sigh */
+   if (i_read_errno == EIO) {
+       /* Sun, Cray, BSD, and others */
+       cc = EXP_EOF;
+   } else if (i_read_errno == EINVAL) {
+       /* Solaris 2.4 occasionally returns this */
+       cc = EXP_EOF;
+   } else {
+       if (i_read_errno == EBADF) {
+       exp_error(interp,"bad spawn_id (process died earlier?)");
+       } else {
+       exp_error(interp,"i_read(spawn_id fd=%d): %s",esPtr->fdin,
+           Tcl_PosixError(interp));
+       if (esPtr->close_on_eof) {
+       exp_close(interp,esPtr);
+       }
+       }
+       return(EXP_TCLERROR);
+       /* was goto error; */
+   }
+    }
+
+    /* EOF, TIMEOUT, and ERROR return here */
+    /* In such cases, there is no need to update screen since, if there */
+    /* was prior data read, it would have been sent to the screen when */
+    /* it was read. */
+    if (cc < 0) return (cc);
+
+    /*
+     * update display
+     */
+
+    size = expSizeGet(esPtr);
+    if (size) write_count = size - esPtr->printed;
+    else write_count = 0;
+    
+    if (write_count) {
+   /*
+    * Show chars to user if they've requested it, UNLESS they're seeing it
+    * already because they're typing it and tty driver is echoing it.
+    * Also send to Diag and Log if appropriate.
+    */
+   expLogInteractionU(esPtr,esPtr->input.buffer + esPtr->printed, write_count);
+       
+   /*
+    * strip nulls from input, since there is no way for Tcl to deal with
+    * such strings.  Doing it here lets them be sent to the screen, just
+    * in case they are involved in formatting operations
+    */
+   if (esPtr->rm_nulls) size = expNullStrip(&esPtr->input,esPtr->printed);
+   esPtr->printed = size; /* count'm even if not logging */
+    }
+    return(cc);
+}

 /* when buffer fills, copy second half over first and */
 /* continue, so we can do matches over multiple buffers */
@@ -2363,7 +2563,12 @@

    /* "!e" means no case matched - transfer by default */
    if (!e || e->transfer) {
-       int remainder = numchars-match;
+       int remainder;
+       if (match > numchars) {
+       match = numchars;
+       eo->matchlen = match;
+       }
+       remainder = numchars-match;
        /* delete matched chars from input buffer */
        esPtr->printed -= match;
        if (numchars != 0) {
@@ -2548,6 +2753,11 @@
     time_t current_time = 0;   /* current time (when we last looked)*/
     time_t end_time;       /* future time at which to give up */

+    double start_time_total_d; /* time at beginning of this procedure */
+    double start_time_d = 0.;  /* time when restart label hit */
+    double current_time_d = 0.;    /* current time (when we last looked)*/
+    double end_time_d;         /* future time at which to give up */
+
     ExpState *last_esPtr;  /* for differentiating when multiple f's */
                /* to print out better debugging messages */
     int last_case;     /* as above but for case */
@@ -2556,8 +2766,9 @@
     int key;           /* identify this expect command instance */
     int configure_count;   /* monitor exp_configure_count */

-    int timeout;       /* seconds */
+    int timeout;       /* seconds   (or milliseconds if less than -1) */
     int remtime;       /* remaining time in timeout */
+    double remtime_d;  /* remaining time in timeout (milliseconds) */
     int reset_timer;       /* should timer be reset after continue? */
     Tcl_Time temp_time;
     Tcl_Obj* new_cmd = NULL;
@@ -2585,7 +2796,9 @@

     Tcl_GetTime (&temp_time);
     start_time_total = temp_time.sec;
+    start_time_total_d = temp_time.sec * 1000. + temp_time.usec / 1000.;
     start_time = start_time_total;
+    start_time_d = start_time_total_d;
     reset_timer = TRUE;

     if (&StdinoutPlaceholder == (ExpState *)clientData) {
@@ -2641,6 +2854,7 @@
     else {
         Tcl_GetTime (&temp_time);
    start_time = temp_time.sec;
+       start_time_d = temp_time.sec * 1000. + temp_time.usec / 1000.;
     }

     if (eg.timeout_specified_by_flag) {
@@ -2669,7 +2883,9 @@
    if (reset_timer) {
        Tcl_GetTime (&temp_time);
        current_time = temp_time.sec;
+       current_time_d = temp_time.sec * 1000. + temp_time.usec / 1000.;
        end_time = current_time + timeout;
+       end_time_d = current_time_d - timeout;
    } else {
        reset_timer = TRUE;
    }
@@ -2677,12 +2893,20 @@

     /* remtime and current_time updated at bottom of loop */
     remtime = timeout;
+   remtime_d = timeout * -1.;

     for (;;) {
-   if ((timeout != EXP_TIME_INFINITY) && (remtime < 0)) {
+
+   if ((timeout > EXP_TIME_INFINITY) && (remtime < 0)) {
+       cc = EXP_TIMEOUT;
+   } else if ((timeout < EXP_TIME_INFINITY) && (remtime_d < 0.)) {
        cc = EXP_TIMEOUT;
    } else {
+       if (timeout >= EXP_TIME_INFINITY) {
        cc = expRead(interp,esPtrs,mcount,&esPtr,remtime,key);
+       } else {
+           cc = expRead_d(interp,esPtrs,mcount,&esPtr,remtime_d,key);
+       }
    }

    /*SUPPRESS 530*/
@@ -2732,7 +2956,9 @@
    if (timeout != EXP_TIME_INFINITY) {
        Tcl_GetTime (&temp_time);
        current_time = temp_time.sec;
+       current_time_d = temp_time.sec * 1000. + temp_time.usec / 1000.;
        remtime = end_time - current_time;
+       remtime_d = end_time_d - current_time_d;
    }
     }