fluidmind
Star God: Fu Star God: Lu Star God: Shou

Command Line Script Setup

A code snippet to make CLI scripts more managable.

Copyright © 2013 by Daniel G. Delaney, Fluid Mind Software

The Command Line Script Setup is an easy way to add test mode, silent mode, and debug mode to your CLI scripts. With the code snippet in place at the top of your Perl, Bash, or PHP CLI scripts, a prompt will appear when you run your script that asks you if you actually want to run the script for real:

Usage: -noprompt, -silent, -test, -debug (self-explanatory parameters)
This script runs in TEST mode by default.
Do you want to run it FOR REAL [y/N]? 

If you just hit the ENTER key, it will default to being in test mode. To run the script for real, you either have to type a "Y" or run the script with the "-noprompt" parameter.

Parameters:

-noprompt
Run script for real, without prompting.
-test
Run script in test mode, without prompting.
-silent
Run script without any output to STDOUT. In silent mode, only output sent to STDERR will be seen.
-debug
Execute any code specified for debugging.

In order for the -silent, -test, and -debug parameters to work, you'll need to add a few conditionals throughout your scripts:

To run your script from a cron job, just add the -noprompt parameter to the command and either pipe the output to a log file or add the -silent parameter to keep cron from sending you an email message every time it runs the script. And since error messages will be output, cron will send you an email only when there was a problem.

Perl Version

Just copy and paste this to the top of your script:

# Perl CLI Script Setup
# @link    http://fluidmind.org/software/cli-setup/ (see for documantation)
# @author  Daniel G. Delaney
# @version 3.3
use constant { false => 0, true => 1 };
my %args = map {$_=>1} @ARGV;
$silent = exists($args{'-silent'}) ? true : false;
$testMode = exists($args{'-test'}) ? true : false;
$debug = exists($args{'-debug'}) ? true : false;
if (!$testMode && !exists($args{'-noprompt'})) {
    print STDERR "\nUsage: -noprompt, -silent, -test, -debug (self-explanatory)\n" .
        "This script runs in TEST mode by default.\n" .
        "Do you want to run it FOR REAL [y/N]? ";
    $testMode = (substr(lc(<STDIN>), 0, 1) eq 'y' ? false : true);
}
@ARGV = grep(!/^(?:-silent|-test|-debug|-noprompt)$/, @ARGV);
sub printNotice { print $_[0] if (!$silent); }
sub printError { print STDERR $_[0]; }
sub printDebug { print STDERR $_[0] if ($debug); }
if ($testMode && !$silent) { print "\n*** RUNNING IN TEST MODE ***\n\n"; }
# End of Perl CLI Script Setup

Example:

printNotice("Deleting user ID $userId.\n");
$sql = 'DELETE FROM users WHERE user_id = ' . $userId;
if (!$testMode) {
    $dbh->do($sql);
    if ($sth->err) {
        printError('Error: There was a problem with the database.');
        printDebug('Debug: ' . $dbh->errstr);
    }
} else {
    printDebug("Would have executed: $sql\n");
}

Bash Version

Just copy and paste this to the top of your script:

# Bash CLI Script Setup
# @link    http://fluidmind.org/software/cli-setup/ (see for documentation)
# @author  Daniel G. Delaney
# @version 3.3
silent=false; testMode=false; debug=false; noprompt=false;
args=()
for arg in "$@"; do
    if   [ "$arg" = "-test" ]; then testMode=true;
    elif [ "$arg" = "-silent" ]; then silent=true;
    elif [ "$arg" = "-debug" ]; then debug=true;
    elif [ "$arg" = "-noprompt" ]; then noprompt=true;
    else args+=($arg)
    fi
done
set -- "${args[@]}"
if [ $testMode == false ] && [ $noprompt == false ]; then
    echo "Usage: -noprompt, -silent, -test, -debug (self-explanatory parameters)" 1>&2
    echo "This script runs in TEST mode by default." 1>&2
    echo -n "Do you want to run it FOR REAL [y/N]? " 1>&2
    read
    if [ "${REPLY:0:1}" = "y" ] || [ "${REPLY:0:1}" = "Y" ]; then testMode=false; else testMode=true; fi
fi
function printNotice { if [ $silent != true ]; then echo $1; fi }
function printDebug { if [ $debug == true ]; then echo $1 1>&2; fi }
function printError { echo $1 1>&2; }
if [ $testMode == true ] && [ $silent == false ]; then echo "*** RUNNING IN TEST MODE ***"; fi
# End of Bash CLI Script Setup
    

Example:

printNotice "Some statement saying what this script will do."

if [ $testMode = false ]; then

    # Do something, and set $error to true if there was an error

    if [ $error = true ]; then
        printError "Report that there was a problem."
        printDebug "Output the technical error message."
    fi
else
    printNotice "Would have done something"
fi

PowerShell Version

PowerShell already has Write-Error and Write-Debug functions. For the -Debug parameter to work, you must have a [CmdletBinding()] line toward to top of your script.

You'll have to have the -NoPrompt, -Test, and -Silent parameter lines in the Param() statement toward the top of your script, before the Begin{} segment.

Copy the CLI Setup snippet itself into the Begin{} segment of your script. If you aren't using Begin{} and Process{} segments, just put it close to the top of your script.

# Copy this to the top of your script (or make sure you have one like it)
[CmdletBinding()]

# Copy this to the top of your script, before the Begin{} segment, 
# or add these parameters to your current Param() statement
Param (
    [switch]$NoPrompt,
    [switch]$Test,
    [switch]$Silent
)

# PowerShell CLI Setup
# @link    http://fluidmind.org/software/cli-setup/ (see for documentation)
# @author  Daniel G. Delaney
# @version 1.2
if (!$Test -and !$NoPrompt) {
    Write-Host ("`nUsage: -NoPrompt, -Silent, -Test, -Debug (self-explanatory)`n" +
        "This script runs in TEST mode by default.")
    $forReal = Read-Host "Do you want to run it FOR REAL [y/N]?"
    $Test = ($forReal.Length -gt 0 -and $forReal.Substring(0,1).ToLower() -eq 'y') ? $false : $true
}
function Write-Notice($message) { if (!$Silent) { Write-Output $message; } }
if ($Test) { $ErrorActionPreference = "Continue"; Write-Notice "`n*** RUNNING IN TEST MODE ***`n`n" }
# End of PowerShell CLI Setup

Example:

Write-Notice("Deleting user ID $userId.")
$sql = 'DELETE FROM users WHERE user_id = ' + $userId
if (!$Test) {
    try {
        $SqlCmd.CommandText = $sql
        $rowsAffected = $SqlCmd.ExecuteNonQuery()
    } catch {
        Write-Error 'There was a problem with the database.'
        Write-Debug $error[0]
    }
} else {
    Write-Notice "Would have executed: $sql";
}

PHP Version

Yes, PHP is great for CLI scripts. And the best part about using this setup code is that you can run the script from within a directory in your website, and it will be able to access the rest of the application as if it's being run through the web server. All you need to do is add a "/.." to the end of the line that defines $_SERVER['DOCUMENT_ROOT'] for each level up from document_root this script is run from. I usually have a lib/bin directory in document_root, or you might just have a bin directory, for CLI scripts.

Just copy and paste this to the top of your script:

/**
 * PHP CLI Script Setup
 * @link    http://fluidmind.org/software/cli-setup/ (see for documentation)
 * @author  Daniel G. Delaney
 * @version 3.3
 */
if (PHP_SAPI == 'cli') {
    // If this is within a subdirectory of a website, add a "/.." to the end
    // of DOCUMENT_ROOT for each level up from the root this script is run from
    $_SERVER['DOCUMENT_ROOT'] = realpath(dirname($_SERVER['PHP_SELF']) . '');
    $_SERVER['SERVER_NAME'] = trim(shell_exec('hostname -f'));
    $_SERVER['SERVER_ADDR'] = gethostbyname($_SERVER['SERVER_NAME']);
    $silent = (in_array('-silent', $argv)) ? true : false;
    $testMode = (in_array('-test', $argv)) ? true : false;
    $debug = (in_array('-debug', $argv)) ? true : false;
    if (!$testMode and !(in_array('-noprompt', $argv) or in_array('-n', $argv))) {
        fputs(STDERR, "Usage: -noprompt, -silent, -test, -debug (self-explanatory parameters)\n" .
            "This script runs in TEST mode by default.\n" .
            "Do you want to run it FOR REAL [y/N]? ");
        $testMode = strtolower(substr(trim(fgets(STDIN)), 0, 1)) == 'y' ? false : true;
    }
    $argv = array_diff($argv, array('-test', '-silent', '-noprompt', '-debug'));
    define('SILENT', $silent); define('TEST_MODE', $testMode); define('DEBUG', $debug);
    function printNotice($message) { if (!SILENT) print $message; }
    function printDebug($message) { if (DEBUG) fputs(STDERR, $message); }
    function printError($message) { fputs(STDERR, $message); }
    if (TEST_MODE and !SILENT) { print "\n*** RUNNING IN TEST MODE ***\n\n"; }
}
/* End of PHP CLI Script Setup */

Example:

printNotice("Deleting user ID $userId.");
$sql = 'DELETE FROM users WHERE user_id = ' . $userId;
if (!TEST_MODE) {
    try {
        $db->exec($sql);
    } catch (Exception $e) {
        printError('Error: There was a problem with the database.');
        printDebug('Debug: ' . $e->getMessage());
    }
} else {
    printNotice("Would have executed: $sql");
}