4

より堅牢なスクリプトを作成するには、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最初の期待値は新しく着信したデータをバッファーに残さないため、機能しません。

4

2 に答える 2

0

パッチ適用は簡単です...ミリ秒単位で負のタイムアウトを使用します(特別な-1を除く):

# set timeout to 100 milliseconds
set timeout -100

以下は、milliExpect.patch... という名前の場合、expect5.45 ディレクトリに移動して実行します。

patch -Np1 -i milliExpect.patch.

次に、通常(tclがconfigureのどこにあるかを規定する必要がある場合があります)...

./configure; make; sudo make install

--- ミリ期待.パッチ ----

--- 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;
    }
     }
于 2014-09-30T21:30:06.377 に答える
0

Tcl インタープリターでバッファーをフラッシュする方法がわかりません。

あなたのユースケースについてはよくわかりませんが、リモートシェルスクリプトの最も信頼できる形式は期待以上であることがわかりました。最も簡単なことは、各送信の最後に a を含めることです#randomnumber#randomnumber生成されたプロセスに送信する最後の行にバッファが同期されていることを確認してください。生成されたプロセスが送信した文字をエコーし​​ない場合、走行距離は異なります。

TCL 実装から Python に移行しても問題ない場合は、 pexpectの純粋な Python 実装が最​​適です。バッファの動作が少し異なるため、慣れるまでに時間がかかります。リモート シェルでコマンドを実行している場合は、python-remoteをお勧めします(これは私が書いたものです) 。

上記で使用している方法でバッファにガスを入れることができます

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

ランダムな文字列を送信して、応答の前にバッファを同期します

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
于 2013-03-07T23:32:55.210 に答える