2013-02-19 73 views
3

要写出更强大的脚本,它以“忘记”的内容指望缓冲器,确保匹配是有用的只在最近接收到的输入来完成:如何做超时少于1秒?

# 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不会工作,因为第一个expect不会将新传入的数据保留在缓冲区中。

回答

0

我不知道如何刷新tcl解释器中的缓冲区。

我不确定你的用例,但是我发现最可靠的远程shell脚本格式超过了期望,最简单的做法是在每次发送结束时包含一个#randomnumber,期望对于#randomnumber,这可以确保将缓冲区同步到发送到派生进程的最后一行。如果产生的过程不回应您发送的字符,您的里程将有所不同。

pexpect开始的纯python实现非常棒,如果您从TCL实现移动到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 
+0

我希望设置一个特殊的shell提示符(例如'(丑陋提示)#')在远程主机上,'expect'在发送任何命令之前提示。 – pynexj 2013-03-19 06:15:20

+0

壳现在总是你的行为。我无法以任何方式修改远程提示。顺便说一句。在shell中,我使用编号PS1 =“(\!)$ PS1”,并保持数字同步。这总是知道哪个提示属于哪个命令。 – user1558113 2013-04-25 10:18:06

-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; 
    } 
    } 
相关问题