piotrmurach / tty-command

Execute shell commands with pretty output logging and capture stdout, stderr and exit status.
https://ttytoolkit.org
MIT License
400 stars 32 forks source link

Feature request: until / line parser #31

Closed aight8 closed 7 years ago

aight8 commented 7 years ago

Following functions I am missing at the most. They can be implemented in pure bash, but they would fit very nicely in this libraries scope.

Until - Stdout evaluation until (bash like)

Raw implementation (bash):

cmd.run 'until [ $(kubectl get pods -n kube-system -l app=helm,name=tiller -o jsonpath="{.items[0].status.containerStatuses[0].ready}") = "true" ]; do
   sleep 2
done'

Better:

cmd.until 'kubectl get pods -n kube-system -l app=helm,name=tiller -o jsonpath="{.items[0].status.containerStatuses[0].ready}', 'Ready'

So this waits until the output of the command is Ready. The stdout which get compared should be striped.

Wait - Log running script line match

Similar but not the same. Long running bash process which output log style lines - it should be possible to wait until a specific pattern match to a line. Example:

cmd.wait 'tail -f /var/log/php.log', /something happened/

Ignore errors (no detailed concept now)

Sometimes shell executions could output some errors which should not cause a raise. A regexp pattern could help here.

piotrmurach commented 7 years ago

I like the concept of intuitive wrappers. I've already added test bash function and I'm open to more. Please do submit PRs if you have working implementation(s). I'm currently taking a leave from OSS until next month - too busy so won't be able to review or work on this.

piotrmurach commented 7 years ago

Hey Sebastian,

I had a closer look at your feature request and I think this will require a bit more work. Fundamentally, there is currently no way to terminate command process directly (only through timeout). I believe this to be a key requirement needing implementation in order to actually stop command and its logging once pattern has been matched etc... Once I have mechanism to arbitrarily kill running command process, I should be able to inject special output processor that will verify messages and if no match is found pass them through to printer for logging; and if match is found kill the command. This is more akin to the wait implementation of a long running child process. In regard to until, I believe this would be sligthly different as I would simply have an infinite loop that checks command result and keeps on executing command until it matches output. Is my thinking correct or am I missing some key points here?

piotrmurach commented 7 years ago

@aight8 I have some good news. I had to rewrite how run method works underneath and expose some api calls. Now it is possible to actually write the requested functionality fairly easily:

begin
  cmd.run('tail -f /var/log/php.log') do |out, err|
    raise ArgumentError if out =~ /pattern/
    # raising inside a command block will exit cleanly, e.i.
    # close any pipes and detach the process
  end
rescue TTY::Command::ExitError
  # we have a match!!!
end

I have also added the above as a wait api call so you can do:

cmd.wait('tail -f /var/log/php.log', /pattern/)

Please see example.

However, I haven't implemented the until version as it can easily be handled by infinite calls to wait:

loop { cmd.wait('cmd to fire', /pattern/) }