vcn/exeg

Library providing a shell command abstraction

v1.3.0 2019-10-25 12:07 UTC

This package is auto-updated.

Last update: 2021-03-25 17:14:44 UTC


README

Exeg is a PHP-library that provides an abstraction for commands. Exeg makes it possible to execute the same command in different environments, for example local (using symfony/process), or over SSH.

Quickstart

<?php

use Vcn\Exeg\Command;
use Vcn\Exeg\Shell;

// Create a command
$command = new Command('cat', ['/etc/hosts']);

// We now have a platform-agnostic representation of the command:
var_dump($command);
echo PHP_EOL;

// Command-instances are immutable. Instead of setters the class has with-methods which create copies of the current
// instance with the specified property changed. Let's demonstrate it by complete rebuilding the command:
$command = $command
    ->withCmd('env')
    ->withArgs([])
    ->withWorkDir('/')
    ->withEnv(['FOO' => 'BAR']);

// withArgs() and withEnv() have an optional second boolean parameter which you can use to append them instead of replacing them
$command = $command->withEnv(['BAR' => 'BAZ'], true);

// Exeg also provides a few utilities to use the commands. For example, let's render the command to a string which has
// been properly escaped, ready for usage in a shell
$shellString = Shell::render($command) . PHP_EOL;

passthru($shellString);

Symfony

Exeg provides an adapter to symfony/process:

<?php

use Vcn\Exeg\Command;
use Vcn\Exeg\Symfony\ExegSymfony;
use Vcn\Exeg\Symfony\ProcessBuffer;

// ExegSymfony is the adapter from Exeg to symfony/process
$exegSymfony = new ExegSymfony();

// This is the command we want to execute.
$command = new Command('cat', ['/etc/hosts']);

// The ExegSymfony-instance can build a Symfony-process for us
$process = $exegSymfony->build($command);

// Symfony uses callbacks to handle process output. Exeg provides utilities to handle them in a few different ways. In
// this case, we want a callback that passes through all data to our own stdout/stderr.
$passthroughCallback = ProcessBuffer::passthrough();

// Run process and display exit code
$exitCode = $process->run($passthroughCallback);
echo "Command exited with code {$exitCode}" . PHP_EOL;

Working directories

When working with paths relative to a directory, Vcn\Exeg\WorkDir makes life a little easier.

<?php

use Vcn\Exeg\WorkDir;

$workDir = new WorkDir('/etc');

// Displays /etc/hosts
$displayHostsCommand = $workDir->command('cat', ['hosts']);

// Displays /etc/hostname
$displayHostnameCommand = $workDir->command('cat', ['hostname']);

Combining commands

Sometimes commands take commands as argument. You can use Shell::render() to generate the command argument.

<?php

use Vcn\Exeg\Command;
use Vcn\Exeg\Shell;
use Vcn\Exeg\Symfony\ExegSymfony;
use Vcn\Exeg\Symfony\ProcessBuffer;

$exegSymfony = new ExegSymfony();

$catHostsCommand = new Command('cat', ['/etc/hosts']);
$bashCommand     = new Command('bash', ['-c', Shell::render($catHostsCommand)]);

$exegSymfony->run($bashCommand, ProcessBuffer::passthrough());

Pipelines

You can construct unix pipelines using a utility-class:

<?php

use Vcn\Exeg\Command;
use Vcn\Exeg\Shell;

$findCmd = new Command(
    'find', [
              // find all files in /home/johndoe ending in *.jpg or *.jpeg
              '/home/johndoe', '-xdev', '-type', 'f', '(', '-iname', '*.jpg', '-o', '-iname', '*.jpeg', ')',
              // use magick to output [filename] [width px] [height px]
              '-exec', 'magick', 'identify', '-format', '%i %w %h\n', '{}', ';',
          ]
);

// Use awk to change output to [w*h] [filename]
$awkCmd = new Command('awk', ['-F', ' ', '{ print $2*$3 " " $1; }']);

// Use numeric sort to the files by number of pixels
$sortCmd = new Command('sort', ['-n']);

$cmd = Shell\Pipeline
    ::first($findCmd)
    ->then($awkCmd)
    ->last($sortCmd);

echo "{$cmd->render()}\n";

SSH

The class Vcn\Exeg\Command\Ssh\Server implements an abstraction of an SSH server.

<?php

use Vcn\Exeg\Command;
use Vcn\Exeg\Command\Ssh;
use Vcn\Exeg\Symfony\ExegSymfony;
use Vcn\Exeg\Symfony\ProcessBuffer;

$exegSymfony = new ExegSymfony();

$sshServer = new Ssh\Server('example.test'); 

$catHostsCommand = new Command('cat', ['/etc/hosts']);
$sshCommand      = $sshServer->adopt($catHostsCommand);

$exegSymfony->run($sshCommand, ProcessBuffer::passthrough());

rsync

The class Vcn\Exeg\Command\Rsync\Factory creates rsync commands.

<?php

use Vcn\Exeg\Command\Rsync;
use Vcn\Exeg\Symfony\ExegSymfony;
use Vcn\Exeg\Symfony\ProcessBuffer;

$exegSymfony = new ExegSymfony();

$srcDir       = '/home/user/src/';
$destDir      = '/home/user/dest/';
$rsyncParams  = Rsync\Params::create()->withArchive();
$rsyncCommand = Rsync\Factory::local($srcDir, $destDir, $rsyncParams);

$exegSymfony->run($rsyncCommand, ProcessBuffer::passthrough());

Rsync + SSH

The factory also contains methods to rsync from/to SSH servers.

<?php

use Vcn\Exeg\Command\Rsync;
use Vcn\Exeg\Command\Ssh;
use Vcn\Exeg\Symfony\ExegSymfony;
use Vcn\Exeg\Symfony\ProcessBuffer;

$exegSymfony = new ExegSymfony();

$srcDir       = '/home/user/src/';
$destServer   = new Ssh\Server('example.test');
$destDir      = '/home/user/dest/';
$rsyncParams  = Rsync\Params::create()->withArchive();
$rsyncCommand = Rsync\Factory::localToSsh($srcDir, $destServer, $destDir, $rsyncParams);

$exegSymfony->run($rsyncCommand, ProcessBuffer::passthrough());