yuloh / expect
Script interactions with interactive terminal applications.
Installs: 2 428
Dependents: 0
Suggesters: 0
Security: 0
Stars: 36
Watchers: 1
Forks: 6
Open Issues: 0
Requires
- php: >=5.4.0
- psr/log: ^1.0
Requires (Dev)
- phpunit/phpunit: 4.*
- squizlabs/php_codesniffer: ~2.3
This package is auto-updated.
Last update: 2021-11-23 07:05:55 UTC
README
This package is a pure PHP alternative to expect, the Unix tool. This package doesn't depend on the PECL package either.
Expect lets you script interactions with interactive terminal applications.
Why?
I wrote this because I wrote an interactive CLI program and needed to write automated tests for it. Apparently people use the real expect for scripting ftp and telnet workflows, so I guess you could use it for that too.
Installation
composer require yuloh/expect
API
Note: all methods return $this for fluent chaining.
spawn(string $cmd, string $cwd = null, LoggerInterface $logger = null)
Spawn a new instance of expect for the given command. You can optionally specify a working directory and a PSR compatible logger to use.
expect(string $output, $timeout = 9999999)
Expect the given text to show up on stdout. Expect will block and keep checking the stdout buffer until your expectation shows up or the timeout is reached, whichever comes first.
You can use shell wildcards to match parts of output.
send(string $msg)
Send the given text on stdin. A newline is added to each string to simulate pressing enter. If you want to just send enter you can do send(PHP_EOL)
Examples
Simple Example
This example opens cat
without any arguments, which will simply echo back everything you type to it.
Yuloh\Expect\Expect::spawn('cat') ->send('hi') ->expect('hi') ->send('yo') ->expect('yo') ->run();
Npm init
This example demonstrates creating a new package.json with npm. Globs are used to match the expectations so we don't need to type them exactly.
Yuloh\Expect\Expect::spawn('npm init') ->expect('*name:*') ->send('package') ->expect('version*') ->send('1.0.0') ->expect('description*') ->send('awesome') ->expect('entry point*') ->send('index.js') ->expect('test command*') ->send('test') ->expect('git repository*') ->send('yuloh/expect') ->expect('keywords*') ->send('awesome') ->expect('author*') ->send('matt') ->expect('license*') ->send('ISC') ->expect('*') ->send('yes') ->run();
Logging
You will probably need logging to figure out what is happening. Expect accepts a PSR compatible logger during instantiation. You can use the Yuloh\Expect\ConsoleLogger
for readable output while writing scripts or debugging. For example, instantiating Expect like this:
Yuloh\Expect\Expect::spawn('cat', getcwd(), new Yuloh\Expect\ConsoleLogger()) ->send('hi') ->expect('hi') ->run();
...would output this to the terminal:
* Sending 'hi⏎' * Expected 'hi', got 'hi'
Exceptions
There are a couple of things that can go wrong while running your process:
- The process can fail to start.
- The process can timeout before your expectation happens.
- The process can halt unexpectedly.
- The process can send EOF before your expectation happens.
If the process fails to start, a RuntimeException
is thrown. The Yuloh\Expect\Exceptions
namespace contains exceptions for the other three scenarios. All of the exceptions extend Yuloh\Expect\FailedExpectationException
so you can just catch that one if you like.
Buffering
Some programs like Composer buffer the output so Expect won't work unless you unbuffer the output. The easiest way to do this is probably using script. Modify your command to pipe through script like this:
# FreeBSD/Darwin (Mac OSX) script -q /dev/null {your-command} # Linux script -c {your-command} /dev/null
Then you can pass that in to Expect:
Expect::spawn('script -q /dev/null ssh localhost') ->expect('*password:') ->send('hunter 2') ->run();
You will probably need to modify expectations when using script, since what you type will show up in stdout too.
Testing
composer test
composer cs