2022年6月18日 星期六

[Linux] expect

expect 指令依據腳本和其它程式互動,預期 (expect) 對方的輸出而給對應的回應。

expect 是擴充 tcl 的直譯式語言,提供 branching 和高階控制架構來引導對話。需要時,使用者可以取得控制直接互動,後來再將控制權交還給 script。expect 實際上可以同時連線多個程式。

構想來自普遍在 uucp、kermit 和其它數據機控制程式的 send/expect sequences.

指令格式

expect [ -dDinN ] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]

讀 cmdfile 取得要執行指令,或在可執行腳本首行放

#!/usr/bin/expect -f

來隱含 invoke。

  • cmdfile:
  • -f cmdfile:prefaces a file from which to read commands from. 只用在可執行腳本首行的 #!,so that other arguments may be supplied on the command line.
  • -b cmdfile:batch 模式,一行一行讀取 cmdfile,例如 stdin 就是用這種方式讀取。預設 cmdfile 是一次讀到記憶體來執行。Note that stdio-buffering may still take place however this shouldn't cause problems when reading from a fifo or stdin.
  • -b - 或 -:cmdfile 檔名用「-」使用 stdin。
  • -c cmds:預先執行的指令。The command should be quoted to prevent being broken up by the shell. 可有多個 -c 按照出現順序執行,或者多個指令用分號隔開使用單一 -c。
  • -d:啟用 some diagnostic 輸出,主要回報內部 activity of commands such as expect and interact。相當於「exp_internal 1」at  the  beginning  of  an  Expect script,  plus the version of Expect is printed.  (The strace command is useful for tracing statements, and the  trace  command  is  useful  for tracing  variable  assignments.)
  • -D:啟用 an interactive debugger.  An integer  value  should follow.   The  debugger will take control before the next Tcl procedure if the value is non-zero or if a ^C is pressed (or a breakpoint is hit, or  other appropriate debugger command appears in the script).  See the README file or SEE ALSO (below) for more information on  the  debugger.
  • -i:causes Expect to interactively prompt for commands instead of reading them from a file.  Prompting is terminated via the exit command or upon EOF.  See interpreter (below) for more information.  -i is assumed if neither a command file nor -c is used.
  • --:表示給 expect 的選項結束,之後的引數直接 pass 給腳本,包括「-」開頭的引數。適合用在腳本的 #! 行。For  example,  the following will leave the original arguments (including the script name) in the variable argv.

    #!/usr/bin/expect --

    Note that the usual getopt(3) and execve(2)  conventions  must  be  observed when adding arguments to the #! line.

  • -N:The  file  $exp_library/expect.rc  is sourced automatically if present, unless the -N flag is used.  (When using Expectk, this option is specified  as  -NORC.)   Immediately  after  this,  the file ~/.expect.rc is sourced automatically, unless the -n flag is used.  If the  environment variable DOTDIR is defined, it is treated as a directory and .expect.rc is read from there.  This sourcing occurs only after executing any -c flags.
  • -v:印版號後離開。

Optional args are constructed into a list and stored  in  the  variable
       named argv.  argc is initialized to the length of argv.

argv0  is  defined to be the name of the script (or binary if no script
       is used).  For example, the following prints out the name of the script
       and the first three arguments:

           send_user "$argv0 [lrange $argv 0 2]\n"

指令

expect 使用 Tcl (Tool Command Language) 提供流程控制 (例如 if, for, break)、expression evaluation、和幾個其它 features such as recursion, procedure definition 等。這裡沒定義的指令是 Tcl 指令 (如 set, if, exec),見 tcl(3)。expect 額外支援的指令如下,除非特別 specified,指令回 empty string。

指令按照字母排序,但新使用者可能要先看 spawn、send、expect、和 interact 較容易。

接下來,用「Expect」表示程式 (Linux 下的指令),「expect」表示 Expect 程式裡的 expect 指令。

close [-slave] [-onexec 0|1] [-i spawn_id]

停止連線到程式,大部分程式偵測到 stdin EOF 後也會自動結束。expectinteract 都會偵測連線的程式結束而隱含 close,但如果用「exec kill $pid」來停止 process,需要明確 close。

-i spawn_id:停止連線到指定的 spawn_id。

-onexec:determines whether the spawn id will  be  closed
             in  any new spawned processes or if the process is overlayed.  To
             leave a spawn id open, use the value 0.  A non-zero integer value
             will force the spawn closed (the default) in any new processes.

-slave:closes the slave associated with the spawn id.
             (See "spawn -pty".)  When the connection is closed, the slave  is
             automatically closed as well if still open.

No  matter whether the connection is closed implicitly or explicitly, you should call wait to clear up the  corresponding  kernel process slot.  close does not call wait since there is no guarantee that closing a process connection will cause it to exit.

debug [[-now] 0|1]

控制 Tcl debugger 來 step through statements、設 breakpoints 等。

no arguments:看 debugger 是否執行中,回 0 表示執行中,1 不在執行中。

1:啟動 debugger。(下個 Tcl statement 開始)

-now 1:馬上啟動 debugger。(在本身 debug 指令中間就啟動) 

0:停止 debugger。

The  debug  command  does  not change any traps. 比較 Expect 的 -D 參數。

disconnect

中斷和親 process 的連繫,繼續在背景執行,會有自己的 process group (可能的話),標準 I/O 導到 /dev/null。

下列 script 預先讀取 priv_prog 需要的 password,然後每小時背景執行 priv_prog。(見 stty 如何關閉 password echoing。)

send_user "password?\ "
expect_user -re "(.*)\n"
for {} 1 {} {
    if {[fork]!=0} {sleep 3600;continue}
    disconnect
    spawn priv_prog
    expect Password:
    send "$expect_out(1,string)\r"
    . . .
    exit
}

使用 disconnect 的好處是在和親 process 的斷線前可以儲存參數,然後用在新建立的 ptys。如果用 shell 的 asynchronous process  feature (&),Expect 沒有機會讀取 the terminal's parameters since the terminal is  already  disconnected  by the time Expect receives control.

exit [-opts] [status]

causes Expect to exit or otherwise prepare to do so.

-onexit:causes the next argument to be used as an exit handler.  Without an argument, the current exit  handler  is  returned.

-noexit:causes Expect to prepare to exit but stop short of actually returning control to the operating system.  The user-defined exit handler is run as well as Expect's own internal handlers.  No further Expect commands should be executed.   This  is useful  if you are running Expect with other Tcl extensions.  The current interpreter (and main window if in  the  Tk  environment) remain  so  that  other Tcl extensions can clean up.  If Expect's exit is called again (however this might occur), the handlers are not rerun.

Upon  exiting,  all  connections to spawned processes are closed. Closure will be detected as an EOF by  spawned  processes.   exit takes  no other actions beyond what the normal _exit(2) procedure does.  Thus, spawned processes that do not check for EOF may continue  to  run.  (A variety of conditions are important to determining, for example, what signals a spawned process will be sent, but   these  are  system-dependent,  typically  documented  under exit(3).)  Spawned processes that continue to run will be  inherited by init.

status  (or 0 if not specified) is returned as the exit status of Expect.  exit is implicitly executed if the end of the script  is reached.

exp_continue [-continue_timer]

The command exp_continue allows expect itself to continue execut‐
             ing rather than  returning  as  it  normally  would.  By  default
             exp_continue  resets  the timeout timer. The -continue_timer flag
             prevents timer from being restarted. (See expect for more  infor‐
             mation.)

exp_internal [-f file] value
             causes  further  commands to send diagnostic information internal
             to Expect to stderr if value is non-zero.  This  output  is  dis‐
             abled  if  value is 0.  The diagnostic information includes every
             character received, and every attempt made to match  the  current
             output against the patterns.

             If the optional file is supplied, all normal and debugging output
             is written to that file (regardless of the value of value).   Any
             previous diagnostic output file is closed.

             The -info flag causes exp_internal to return a description of the
             most recent non-info arguments given.

exp_open [args] [-i spawn_id]
             returns a Tcl file identifier that corresponds  to  the  original
             spawn  id.   The  file  identifier can then be used as if it were
             opened by Tcl's open command.  (The spawn id should no longer  be
             used.  A wait should not be executed.

             The  -leaveopen  flag leaves the spawn id open for access through
             Expect commands.  A wait must be executed on the spawn id.

exp_pid [-i spawn_id]
             returns the process id corresponding  to  the  currently  spawned
             process.  If the -i flag is used, the pid returned corresponds to
             that of the given spawn id.

exp_send
             is an alias for send.

exp_send_error
             is an alias for send_error.

exp_send_log
             is an alias for send_log.

exp_send_tty
             is an alias for send_tty.

exp_send_user
             is an alias for send_user.

exp_version [[-exit] version]
             is useful for assuring that the script  is  compatible  with  the
             current version of Expect.

             With  no  arguments,  the  current version of Expect is returned.
             This version may then be encoded in your script.  If you actually
             know  that you are not using features of recent versions, you can
             specify an earlier version.

             Versions consist of three numbers separated by  dots.   First  is
             the  major number.  Scripts written for versions of Expect with a
             different major number will almost certainly not work.   exp_ver‐
             sion returns an error if the major numbers do not match.

             Second is the minor number.  Scripts written for a version with a
             greater minor number than the current  version  may  depend  upon
             some new feature and might not run.  exp_version returns an error
             if the major numbers  match,  but  the  script  minor  number  is
             greater than that of the running Expect.

             Third  is  a number that plays no part in the version comparison.
             However, it is incremented when the Expect software  distribution
             is changed in any way, such as by additional documentation or op‐
             timization.  It is reset to 0 upon each new minor version.

             With the -exit flag, Expect prints an error and exits if the ver‐
             sion is out of date.

expect [[-opts] pat1 body1] ... [-opts] patn [bodyn]

等候 spawn 的 process 輸出直到符合其中一個 pattern、EOF、或 timeout。If the final body is empty, it may be omitted.

Patterns  from  the most recent expect_before command are implicitly used before any other patterns.  Patterns from the most  recent  expect_after  command  are  implicitly used after any other patterns.

If the arguments to the entire expect statement 需要跨多行,可以 "braced" into one 來避免每行以 backslash 結束。the usual Tcl substitutions will occur despite the braces。

如果 pattern 是 keyword eof,the corresponding body is executed upon end-of-file.  If a pattern is the keyword timeout, the corresponding  body is executed upon timeout.  If no timeout keyword is used, an implicit null action is executed  upon  timeout。預設 timeout 是 10 秒,但 may be set, for example to 30, by the command "set timeout 30".  An infinite  timeout  may be designated by the value -1.  If a pattern is the keyword default, the corresponding  body  is  executed  upon  either timeout or end-of-file.

If  a  pattern  matches, then the corresponding body is executed. expect returns the result of the body (or the empty string if  no pattern matched).

Each time new output arrives, it is compared to each  pattern  in
             the  order  they are listed.  Thus, you may test for absence of a
             match by making the last pattern something guaranteed to  appear,
             such  as  a  prompt.  In situations where there is no prompt, you
             must use timeout (just like you would  if  you  were  interacting
             manually).

Patterns  are  specified in three ways. 預設用 Tcl 的 string match 指令,(Such patterns are
             also  similar  to C-shell regular expressions usually referred to
             as "glob" patterns).  The -gl flag may may  be  used  to  protect
             patterns  that  might otherwise match expect flags from doing so.
             Any pattern beginning with a "-" should be  protected  this  way.
             (All strings starting with "-" are reserved for future options.)

For example, the following fragment looks for a successful login.
             (Note that abort is presumed to be a procedure defined  elsewhere
             in the script.)

                 expect {
                     busy               {puts busy\n ; exp_continue}
                     failed             abort
                     "invalid password" abort
                     timeout            abort
                     connected
                 }

Quotes  are  necessary  on the fourth pattern since it contains a space, which would otherwise separate the pattern  from  the  action. More information on forming glob-style patterns can be found in the Tcl manual.

Regexp-style patterns follow the syntax defined by  Tcl's  regexp command.  regexp patterns are introduced with the  flag  -re.   The  previous  example  can  be rewritten using a regexp as:

                 expect {
                     busy       {puts busy\n ; exp_continue}
                     -re "failed|invalid password" abort
                     timeout    abort
                     connected
                 }

Both  types  of  patterns are "unanchored".  This means that patterns do not have to match the entire string, but can  begin  and
             end  the match anywhere in the string (as long as everything else
             matches).  Use ^ to match the beginning of a  string,  and  $  to
             match  the  end.   Note  that if you do not wait for the end of a
             string, your responses can easily end up in  the  middle  of  the
             string  as they are echoed from the spawned process.  While still
             producing correct results, the output can look unnatural.   Thus,
             use of $ is encouraged if you can exactly describe the characters
             at the end of a string.

Note that in many editors, the ^ and $ match  the  beginning  and end  of  lines  respectively. However, because expect is not line oriented, these characters match the beginning  and  end  of  the data  (as opposed to lines) currently in the expect matching buffer.  (Also, see the note below on "system indigestion.")

-ex:the pattern  to  be  matched  as  an  "exact" string.   No  interpretation  of  *, ^, etc is made (although the usual Tcl conventions must still be  observed).   Exact  patterns are always unanchored.

-nocase:大寫輸出視為小寫。不影響 pattern。

While  reading  output,  more  than  2000 bytes can force earlier
             bytes to be "forgotten".  This may be changed with  the  function
             match_max.  (Note that excessively large values can slow down the
             pattern matcher.)  If patlist is full_buffer,  the  corresponding
             body  is  executed  if  match_max bytes have been received and no
             other patterns have matched.  Whether or not the full_buffer key‐
             word  is  used,  the  forgotten  characters  are  written  to ex‐
             pect_out(buffer).

If patlist is the keyword null, and nulls are  allowed  (via  the
             remove_nulls  command),  the  corresponding body is executed if a
             single ASCII 0 is matched.  It is not possible to match  0  bytes
             via glob or regexp patterns.

Upon matching a pattern (or eof or full_buffer), any matching and
             previously  unmatched  output  is  saved  in  the  variable   ex‐
             pect_out(buffer).   Up to 9 regexp substring matches are saved in
             the variables expect_out(1,string) through  expect_out(9,string).
             If  the  -indices flag is used before a pattern, the starting and
             ending indices (in a form suitable for lrange) of the 10  strings
             are   stored   in   the  variables  expect_out(X,start)  and  ex‐
             pect_out(X,end) where X is a digit, corresponds to the  substring
             position  in  the  buffer.  0 refers to strings which matched the
             entire pattern and is generated for glob patterns as well as reg‐
             exp  patterns.   For example, if a process has produced output of
             "abcdefgh\n", the result of:

                 expect "cd"

is as if the following statements had executed:

                 set expect_out(0,string) cd
                 set expect_out(buffer) abcd

and "efgh\n" is left in the output buffer.  If a process produced the output "abbbcabkkkka\n", the result of:

                 expect -indices -re "b(b*).*(k+)"

is as if the following statements had executed:

                 set expect_out(0,start) 1
                 set expect_out(0,end) 10
                 set expect_out(0,string) bbbcabkkkk
                 set expect_out(1,start) 2
                 set expect_out(1,end) 3
                 set expect_out(1,string) bb
                 set expect_out(2,start) 10
                 set expect_out(2,end) 10
                 set expect_out(2,string) k
                 set expect_out(buffer) abbbcabkkkk

and "a\n" is left in the output buffer.  The pattern "*" (and -re ".*") will flush the output buffer without reading any more  output from the process.

Normally,  the matched output is discarded from Expect's internal
             buffers.  This may be prevented by prefixing a pattern  with  the
             -notransfer  flag.  This flag is especially useful in experiment‐
             ing (and can be abbreviated to "-not" for convenience  while  ex‐
             perimenting).

The  spawn  id  associated  with  the  matching output (or eof or
             full_buffer) is stored in expect_out(spawn_id).

-timeout:the current expect command  to  use  the following  value  as  a timeout instead of using the value of the timeout variable.

By default, patterns are matched against output from the  current
             process,  however  the -i flag declares the output from the named
             spawn_id list be matched against any following  patterns  (up  to
             the  next  -i).   The spawn_id list should either be a whitespace
             separated list of spawn_ids or a variable  referring  to  such  a
             list of spawn_ids.

For example, the following example waits for "connected" from the current process, or "busy", "failed" or "invalid  password"  from the spawn_id named by $proc2.

                 expect {
                     -i $proc2 busy {puts busy\n ; exp_continue}
                     -re "failed|invalid password" abort
                     timeout abort
                     connected
                 }

The  value  of  the  global  variable any_spawn_id may be used to match patterns to any spawn_ids that are named with all other  -i flags in the current expect command.  The spawn_id from a -i flag with no associated pattern (i.e., followed immediately by another -i)  is  made  available to any other patterns in the same expect command associated with any_spawn_id.

The -i flag may also name a global variable  in  which  case  the
             variable is read for a list of spawn ids.  The variable is reread
             whenever it changes.  This provides a way  of  changing  the  I/O
             source  while  the  command  is in execution.  Spawn ids provided
             this way are called "indirect" spawn ids.

Actions such as  break  and  continue  cause  control  structures
             (i.e.,  for,  proc)  to  behave  in  the  usual way.  The command
             exp_continue allows expect itself to  continue  executing  rather
             than returning as it normally would.

This  is  useful  for  avoiding explicit loops or repeated expect
             statements.  The following example is part of a fragment to auto‐
             mate  rlogin.   The  exp_continue avoids having to write a second
             expect statement (to look for the prompt  again)  if  the  rlogin
             prompts for a password.

                 expect {
                     Password: {
                         stty -echo
                         send_user "password (for $user) on $host: "
                         expect_user -re "(.*)\n"
                         send_user "\n"
                         send "$expect_out(1,string)\r"
                         stty echo
                         exp_continue
                     } incorrect {
                         send_user "invalid password or account\n"
                         exit
                     } timeout {
                         send_user "connection to $host timed out\n"
                         exit
                     } eof {
                         send_user \
                             "connection to host failed: $expect_out(buffer)"
                         exit
                     } -re $prompt
                 }

For  example,  the  following fragment might help a user guide an
             interaction that is already totally automated.  In this case, the
             terminal  is put into raw mode.  If the user presses "+", a vari‐
             able is incremented.  If "p" is pressed, several returns are sent
             to  the process, perhaps to poke it in some way, and "i" lets the
             user interact with the process, effectively stealing away control
             from  the script.  In each case, the exp_continue allows the cur‐
             rent expect to continue pattern matching after executing the cur‐
             rent action.

                 stty raw -echo
                 expect_after {
                     -i $user_spawn_id
                     "p" {send "\r\r\r"; exp_continue}
                     "+" {incr foo; exp_continue}
                     "i" {interact; exp_continue}
                     "quit" exit
                 }

By  default, exp_continue resets the timeout timer.  The timer is
             not restarted, if exp_continue is called with the -continue_timer
             flag.

expect_after [expect_args]
works  identically  to  the expect_before except that if patterns
             from both expect and expect_after can match, the  expect  pattern
             is used.  See the expect_before command for more information.

expect_background [expect_args]

takes  the  same  arguments as expect, however it returns immedi‐
             ately.  Patterns are tested whenever new input arrives.  The pat‐
             tern timeout and default are meaningless to expect_background and
             are silently discarded.  Otherwise, the expect_background command
             uses  expect_before  and  expect_after  patterns just like expect
             does.

             When expect_background actions are  being  evaluated,  background
             processing for the same spawn id is blocked.  Background process‐
             ing is unblocked when the  action  completes.   While  background
             processing is blocked, it is possible to do a (foreground) expect
             on the same spawn id.

             It is not possible to execute an  expect  while  an  expect_back‐
             ground is unblocked.  expect_background for a particular spawn id
             is deleted by declaring a new  expect_background  with  the  same
             spawn  id.   Declaring  expect_background with no pattern removes
             the given spawn id from the ability  to  match  patterns  in  the
             background.

expect_before [expect_args]

takes  the  same  arguments as expect, however it returns immediately.  Pattern-action pairs from the most  recent  expect_before
             with  the same spawn id are implicitly added to any following ex‐
             pect commands.  If a pattern matches, it is treated as if it  had
             been  specified  in the expect command itself, and the associated
             body is executed in the context of the expect command.   If  pat‐
             terns  from  both  expect_before  and  expect  can match, the ex‐
             pect_before pattern is used.

             If no pattern is specified, the spawn id is not checked  for  any
             patterns.

             Unless  overridden  by  a  -i  flag, expect_before patterns match
             against the spawn id defined at the time that  the  expect_before
             command was executed (not when its pattern is matched).

             The  -info flag causes expect_before to return the current speci‐
             fications of what patterns it will match.  By default, it reports
             on  the current spawn id.  An optional spawn id specification may
             be given for information on that spawn id.  For example

                 expect_before -info -i $proc

             At most one spawn id specification may be given.  The flag -indi‐
             rect  suppresses  direct  spawn  ids that come only from indirect
             specifications.

             Instead of a spawn id specification, the flag "-all"  will  cause
             "-info" to report on all spawn ids.

             The output of the -info flag can be reused as the argument to ex‐
             pect_before.

expect_tty [expect_args]

類似 expect,但讀 /dev/tty。

expect_user [expect_args]

類似 expect,但讀 stdin。

fork

分叉出子 process,成功時在子 process 回 0,在親 process 回子 process ID。失敗時,回 -1,不會建立子 process。

子 processes exit via the exit command, just like the original process. 子 processe 仍會寫 log 檔,親子都紀錄可能會混淆。

有些 pty 實作在多個 reader 或 writer 時會困惑,即使只是短暫的,因此 fork 後 spawning processes 較安全。

[待續]

參考

  1. man expect
  • tcl 是一個高階的電腦語言,語法類似 shell script 與 C 語言之間。而 tk 是一個 GUI 發展工具,有 buttons、menus、listboxes、 scrollbars 等等。
  • 書《Exploring Expect》介紹 expect 和 tcl。
  • Expectk 是 Expect 和 Tk 的混合,使用長參數名稱。libexpect 可以直接和 C 或 C++ 使用,而不用 Tcl。
  • 待讀:Tcl/Tk 學習筆記

沒有留言:

張貼留言

SIP header Via

所有 SIP 訊息 都要有 Via,縮寫 v。一開始的 UAC 和後續途經的每個 proxy 都會疊加一個 Via 放傳送的位址,依序作為回應的路徑。 格式 sent-protocol sent-by [ ;branch= branch ][ ; 參數 ...] s...