Search code examples
linuxtclexpect

how to pass arguments to expect -c script?


I want to run a temporary expect script with arguments and leave stdin open:

expect -c 'spawn {*}$argv' /dev/null echo hello world

However this results in

can't read "argv": no such variable
    while executing
"spawn {*}$argv"

How do I execute it?


Solution

  • See the source code for args parsing:

     656 void
     657 exp_parse_argv(interp,argc,argv)
     658 Tcl_Interp *interp;
     659 int argc;
     660 char **argv;
     661 {
     ...
    
     703     while ((c = getopt(argc, argv, "+b:c:dD:f:inN-v")) != EOF) {
     704         switch(c) {
     ...
    
     711         case 'c': /* command */
     712             exp_cmdlinecmds = TRUE;
     713             rc = Tcl_Eval(interp,optarg);
     714             if (rc != TCL_OK) {
     715                 expErrorLogU(exp_cook(Tcl_GetVar(interp,"errorInfo",TCL_GLOBAL_ONLY),(int *)0));
     716                 expErrorLogU("\r\n");
     717             }
     718             break;
     ...
    
     773     }
     ...
    
     850     /* collect remaining args and make into argc, argv0, and argv */
     851     sprintf(argc_rep,"%d",argc-optind);
     852     Tcl_SetVar(interp,"argc",argc_rep,0);
     853     expDiagLog("set argc %s\r\n",argc_rep);
     854
     855     if (exp_cmdfilename) {
     856         Tcl_SetVar(interp,"argv0",exp_cmdfilename,0);
     857         expDiagLog("set argv0 \"%s\"\r\n",exp_cmdfilename);
     858     } else {
     859         Tcl_SetVar(interp,"argv0",exp_argv0,0);
     860         expDiagLog("set argv0 \"%s\"\r\n",exp_argv0);
     861     }
     862
     863     args = Tcl_Merge(argc-optind,argv+optind);
     864     expDiagLogU("set argv \"");
     865     expDiagLogU(args);
     866     expDiagLogU("\"\r\n");
     867     Tcl_SetVar(interp,"argv",args,0);
     868     Tcl_Free(args);
     869
     870     exp_interpret_rcfiles(interp,my_rc,sys_rc);
     871 }
    

    It runs in this order:

    • Line 703, in the while loop, -c command (line 711) is run with Tcl_Eval() (line 713).
    • Line 852 sets Tcl's var argc.
    • Line 856/859 sets argv0.
    • Line 867 sets argv.

    So when -c command is run, argv0/argc/argv are not yet available.


    This can also be shown with expect -d:

    $ expect -d -c 'puts "now in -c command" ' /dev/null foo bar
    expect version 5.45
    now in -c command                  <====
    argv[0] = expect  argv[1] = -d  argv[2] = -c  argv[3] = puts "now in -c command"
    argv[4] = /dev/null  argv[5] = foo  argv[6] = bar
    set argc 2                         <====
    set argv0 "/dev/null"              <====
    set argv "foo bar"                 <====
    executing commands from command file /dev/null