0

I am trying to write an expect script that once started will watch my terminal and run a command every time that it finds a match.

Is this even possible with expect? If so, would expect_background be the right thing to use?

EDIT: For clarity, I am trying to expect the output from my terminal, not the input...

  • I am really curious to know if there is a better tool to do this... – Questionmark Mar 10 '16 at 20:02
2

Yes, you can do this just with bash: redirect stdout to a tee process that can do something when your patterns are seen. tee is good because it also puts stdout on stdout.

exec 1> >(tee >(awk '/foobar/ {print "*** DING DING ***"}'))

This sends stdout to process substitution 1, tee, which copies it's stdin to both stdout and process substitution 2, an awk script that does something. You could put your code, or the path to a script, in the inner process substitution.

To demonstrate

$ echo this is foobar stuff
this is foobar stuff
$ exec 1> >(tee >(awk '/foobar/ {print "*** DING DING ***"}'))
$ date
Thu Mar 10 16:37:14 EST 2016
$ pwd
/home/jackman
$ echo this is foobar stuff
this is foobar stuff
*** DING DING ***
  • Great! Now how do I stop it? – Questionmark Mar 10 '16 at 21:53
  • Easiest would be exec 1> /dev/stdout – glenn jackman Mar 10 '16 at 21:56
  • Can this solution only find the matches if they are in the user input? – Questionmark Mar 10 '16 at 21:59
  • Or,before starting, copy stdout exec 3>&1 then install the stdout catcher then shut it off with exec 1>&3 and throw out the copy exec 3>&- – glenn jackman Mar 10 '16 at 22:00
  • No, it's only looking at stdout. Can't test now but it should ding for printf "%s%s\n" foo "bar" – glenn jackman Mar 10 '16 at 22:02
1
#!/usr/bin/env expect
#
# Adapted from autoexpect(1). Matches stuff from user.

if {[llength $argv] == 0} {
  send_user "Usage: $argv0 command \[cmd args...]\n"
  exit 64
}

match_max 100000
set userbuf ""

proc input {c} {
  global userbuf

  send -- $c
  append userbuf $c

  # if see this pattern in the input, execute command
  # NOTE may well muss up the terminal display, depending
  # on the output, etc.
  if {[regexp "echo.foo\r" $userbuf]} {
    system "uptime"
  # trim buffer between commands so not chewing up all the memory
  } elseif {[regexp "\r" $userbuf]} {
    set userbuf ""
  }
}

spawn -noecho $argv

interact {
  # match incoming characters, send to buffer builder (otherwise
  # if just "echo foo" here that input will vanish until expect
  # can run a command, or fails the match, which isn't nice.
  -re . { input $interact_out(0,string) }
  eof { return }
}

And the other way is simpler (but beware the shell echo).

#!/usr/bin/env expect
#
# Also adapted from autoexpect(1).

if {[llength $argv] == 0} {
  send_user "Usage: $argv0 command \[cmd args...]\n"
  exit 64
}

match_max 100000

proc output {s} {
  send_user -raw -- $s

  # NOTE shells tend to echo commands as they are typed, which may
  # cause double-triggers depending on the expression being looked
  # for an what exactly is passed to this call from the echos.
  if {[regexp "foo" $s]} {
    system "uptime"
  }
}

spawn -noecho $argv

interact {
  -o -re .+ { output $interact_out(0,string) }
  eof { return }
}
  • See my edit... :) Sorry for not making that clear... Interesting answer though. – Questionmark Mar 10 '16 at 20:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.