Automatically update all WordPress plugins from Bash

Automatically update all WordPress plugins from Bash

I’ve updated the script radically. WordPress had changed their site structure so that the older script would fail. With the latest updates, it should still work.

Latest version:
Writeup
Download

I was searching for an idea on how to automatically update all my WordPress plugins from Linux command line. Google introduced me to a script by Ventz Petkov. It worked very well with minor modifications. This script could automatically download all plugins from WordPress.org according to the subdirectories in the WordPress plugin directory.

However a minor drawback of the script is that it would overwrite all plugins without checking whether our plugins are already up to date. I thought this was a waste of resources, since the obvious use of such a script is to get it to run as a cron job at regular intervals. There is no need to keep downloading the same versions of plugins that you already have, countless number of times. Even Petkov has conceded as much, stating that such an addition would be quite useful.

So there you are, my first perl script. I would like to record due credits to Petkov for his method of parsing the wordpress.org website, and his framework. All that I’ve done is to add code for comparing the plugin versions that are in your plugins directory, with those on the website to see if there are available updates. If there are, only then are the files downloaded.

The latest version of the script can always be downloaded from here (New link).

The script can take command line argument. Argument #1 is the path to plugin directory. Argument #2 is the plugin to update (meaning you can manually update one plugin at a time), and is a remnant of the original script. For my purpose, I wont be using Argument #2, since I intend the script to be run automatically, updating as when updates are found.

Screenshots:

Script command line options:

--help or -h: Display this help message
--source=/path/to/plugindirectory or -s/path/to/plugindirectory
	Multiple folders can be specified at a time.
	Eg: ./updater.pl --source=/var/www/virtual/joel.co.in/vettathu.com/htdocs/wp-content/plugins,/var/www/virtual/joel.co.in/drjoel.in/htdocs/wp-content/plugins
--version or -v: Display script version information
--plugin=PluginName or -pPluginName: Specify one or more plugins to process instead of all plugins under source directory. To specify more than one plugin,
you can either repeat this option, like:
	Eg: ./updater.pl --plugin=nextgen-gallery --plugin=genesis-beta-tester
	  Or,
you can alternately specify a comma seperated list of plugins, like:
	Eg: ./updater.pl --plugin=nextgen-gallery,genesis-beta-tester
--wp-path= or -wp: Specify wordpress root directory instead of plugin directory
	Eg: ./updater.pl --wp-path=/var/www/virtual/joel.co.in/droidzone.in/htdocs,/var/www/virtual/joel.co.in/vettathu.com/htdocs

Examples:

/root/wordpress_plugin_updater/updater.pl -w/var/www/virtual/joel.co.in/droidzone.in/htdocs

/root/wordpress_plugin_updater/beta.pl -s/var/www/virtual/joel.co.in/droidzone.in/htdocs/wp-content/plugins

If running from cron:

/usr/bin/perl /path/to/updater.pl -s/var/www/virtual/joel.co.in/vettathu.com/htdocs/wp-content/plugins

An example crontab entry would be:

* */6 * * * /usr/bin/perl /root/bash-advanced-scripts/updater.pl -s/var/www/virtual/joel.co.in/drjoel.in/htdocs/wp-content/plugins >> /root/wordpress_update.log

Here the script is being run sixth hourly every day, and a log appended. You can have multiple entries for multiple blogs.

The following line will automatically flush log files, keeping last 6 days of backups:

0 0 */3 * * mv /root/wordpress_update.log /root/wordpress_update.log.old

You can see the list of updated plugins by:

[[email protected]] ~/temp #cat /root/wordpress_update.log | grep -in 'updating' --color
83:Processing plugin: easy-contact-forms | Local version 1.4 | Remote version 1.4.2 | Updating now..
158:Processing plugin: wordpress-importer | Local version 0.6 | Remote version 0.6.1 | Updating now..
68527:Processing plugin: fancier-author-box | Local version 1.0.5 | Remote version 1.0.6.1 | Updating now..
68910:Processing plugin: fancier-author-box | Local version 1.0.5 | Remote version 1.0.6.1 | Updating now..
90869:Processing plugin: simple-backup | Local version 2.7.1 | Remote version 2.7.2 | Updating now..
91357:Processing plugin: look-see-security-scanner | Local version 13.01 | Remote version 13.04 | Updating now..
91740:Processing plugin: look-see-security-scanner | Local version 13.01 | Remote version 13.04 | Updating now..
113638:Processing plugin: bulletproof-security | Local version .48 | Remote version .48.1 | Updating now..
113686:Processing plugin: bulletproof-security | Local version .48 | Remote version .48.1 | Updating now..
113718:Processing plugin: bulletproof-security | Local version .48 | Remote version .48.1 | Updating now..

 

The current version as of 31/03/13 is included below:

#!/usr/bin/perl -w
# Original Author: Ventz Petkov
# Last updated by Original Author on
# Date: 06-15-2011
# Last: 12-20-2012
# Version: 2.0
#
# Rewritten by: Droidzone
# Date 31-03-2013 www.droidzone.in
# Version 3.0.0.2
# Usage:
# ./update-wp-plugins.pl
#   or
# ./update-wp-plugins.pl registered-name-of-plugin
# (and this works to update an exiting plugin or download+install a new one)

$progversion="3.0.0.2";
$debugmode=0;
use Term::ANSIColor;
print color 'bold blue';
print "Wordpress Plugin Updater script v$progversion.\n";
print color 'reset';
&argparser(@$ARGV);

if(!defined($ARGV[0])) {
    dprint ("No arguments supplied for folder path");
	$path="/var/www/virtual/joel.co.in/vettathu.com/htdocs/wp-content/plugins";
	dprint ("Path was specified on the command line\n");
}
else {
	#my @values = split(',', $ARGV[0]);	
	#foreach my $val (@values) 
	$path=$ARGV[0];
	print "Working on directory: $path\n";	    
    if ( -d $path )
    {
		dprint ("Path verified\n");
    }
    else
    {
		dprint ("The path does not exist\n");
	exit;
    }
}

dprint ("The path was set as ".$path."\n");
print "Plugin directory:$path\n";
chdir($path);

use WWW::Mechanize;
#use File::Find;
my $mech = WWW::Mechanize->new();
$mech->agent_alias( 'Mac Safari' );
my $wp_base_url = "http://wordpress.org/extend/plugins";

################################################
# Add New plugins Here:
# Format:
#   'registered-name-of-plugin',
#
my (@plugins, @plugins_notfound, @filepath, @pluginversion);

use File::Find::Rule;
use File::Spec;

my @folders = File::Find::Rule->directory->maxdepth(1)
    ->in( $path )
    ;

foreach my $i (@folders){
    ( $volume, $directories, $file ) = File::Spec->splitpath( $i );
	undef $volume, $directories;
    if ( $file ne "plugins" )
    {
        push @plugins, $file;
		push @filepath, $i;
		#print "Path: ".$i."\t Name: ".$file."\n";
    }
}

for (my $i = 0; $i < @plugins ; $i++ ) 
{
    dprint ("Processing ".$plugins[$i]."...");
    #print "Filename:".$filepath[$i]."/".$plugins[$i].".php"."\n";
    $filename=$filepath[$i]."/".$plugins[$i].".php";
	$varfound=0;
    if ( -f $filename ) 
    {
		dprint ("Meta File found at default location.\n");				
		open("txt", $filename);
		while($line = <txt>) 
		{			
			if ( $line =~ /^\s*\**\s*\bVersion:(.*)/i )
			{
				$pluginversion[$i]=$1;
				dprint ("Version found in file ".$filename);						
				$varfound=1;										
				dprint ($pluginversion[$i]."\n");
				dprint ("Array Num ".$i." Stored plugin name:".$plugins[$i]." Version found and stored ".$pluginversion[$i]);
			}
		}
		close("txt");			
    }
    else
    {
		$searchpath=$path."/".$plugins[$i];
		@files = <$searchpath/*.php>;
		dprint ("Search path is ".$searchpath."\n");
OUT: 	foreach $file (@files) 
		{
			dprint ("Checking alternate php file: ".$file."\n");
			open("txt", $file);
			while($line = <txt>) 
			{
				#  $line =~ /^[\s\*]*Version:(.*)/i
				# Discussion on regex: http://stackoverflow.com/questions/15728671/perl-regex-logic-error
				if ( $line =~ /^\s*\**\s*\bVersion:(.*)/i )
				{
					$pluginversion[$i]=$1;
					dprint ("Version found in file ".$file);						
					$varfound=1;										
					dprint ($pluginversion[$i]."\n");
					dprint ("Array Num ".$i." Stored plugin name:".$plugins[$i]." Version found and stored ".$pluginversion[$i]);
					last OUT;
				}
			}
			close("txt");	
		}
    }
	push @plugins_notfound, $plugins[$i];
}

if ( ! $varfound) 
{
	print "\nCould not parse version no from the follwing plugins:\n";
	for (my $i = 0; $i < @plugins_notfound ; $i++ ) 
	{
		print $i." ".$plugins_notfound[$i]."\n";
	}
}
else 
{
	dprint ("We found all version numbers");
}
print color 'red';
#print colored("Summary of scanning plugin directory", 'red'), "\n";
print "Summary of scanning plugin directory\n";
print "------------------------------------\n";
printf("%-4s %-45s %3s\n", "No", "Name", "Version");
print color 'reset';
#print "No:\tName\tVersion\n";
for (my $i = 0; $i < @plugins ; $i++ ) 
{
	$v = $pluginversion[$i];
	$v =~ s/[^a-zA-Z0-9\.]*//g;	
	printf("%-4s %-45s %3s\n", $i, $plugins[$i], $v );
	#print "$i\t$plugins[$i]\t$pluginversion[$i]\n";
}
print "\n";

################################################
$pluginsdone=0;

if(defined($ARGV[1])) {
    my $name = $ARGV[1];
    &update_plugin($name);
}
else {
	$i=-1;
    for my $name (@plugins) {
		$i++;
        &update_plugin($name,$i);
    }
}

print "Plugin updation completed successfully.\n";

if ( $pluginsdone > 1 )
{
	print "$pluginsdone plugin(s) were updated.\n";
}
else
{
	print "Plugins were already up-to-date. Nothing done.\n";
}

sub update_plugin {

    my $name = $_[0];
	my $index = $_[1];
    my $url = "$wp_base_url/$name";
    $mech->get( $url );
    my $page = $mech->content;
	$url="";
    my ($version,$description,$file) = "";
    if($page =~ /.*<p class="button"><a itemprop='downloadUrl' href='(.*)'>Download Version (.*)<\/a><\/p>.*/) 
	{
		$url = $1;
		$version = $2;
		if($page =~ /.*<p itemprop="description" class="shortdesc">\n(\s+)?(.*  
	)(\s+)(\t+)?<\/p>.*/) 
		{
			$description = $2;
		}
		if($url =~ /http:\/\/downloads\.wordpress\.org\/plugin\/(.*)/) 
		{
			$file = $1;
		}
    }	
	$oldversion = $pluginversion[$index];
	$version =~ s/[^a-zA-Z0-9\.]*//g;	
	$oldversion =~ s/[^a-zA-Z0-9\.]*//g;

    print "Processing plugin: ";
	print colored($name, 'green');	
	print " | Local version ";
	print colored($oldversion, 'green');		
	print " | Remote version ";

	if ( $version eq $oldversion )
	{
		print colored($version, 'green');	
		print " | ";
		print colored("Already update\n", 'green');
	}
	else
	{
		print colored($version, 'red');	
		print " | ";
		$pluginsdone++;
		print colored("Updating now..\n\n", 'blue');
		#print "Updating plugin $name now";
		`/bin/rm -f $file`; print "Downloading: \t$url\n";
		`/usr/bin/wget -q $url`; print "Unzipping: \t$file\n";
		`/usr/bin/unzip -o $file`; 
		print colored("Installed: \t$name\n\n", 'green');		
		`/bin/rm -f $file`;
	}
}

sub read_extract
{
	my $pl_version="";
    open("txt", my $file=$_[0]);
    while($line = <txt>)
    {
		for ($line)
		{
		 s/^\s+//;
		 s/\s+$//;
		}			
        if ( $line =~ /^Version:|^version:|^\* Version:/ )
        {
            $pl_version=&extract_version($line);
        }
    }
    close("txt");
	$pl_version;
}

sub extract_version
{
    my $line=$_[0];
    $string=substr($line,rindex($line, ":")+1);
    for ($string)
    {
     s/^\s+//;
     s/\s+$//;
    }
    $string;
}

sub dprint
{
	$debugtext=$_[0];
	if ($debugmode) 
	{
		print $debugtext."\n";
	}
}	

sub print_help 
{
	print "\nCommand syntax:";
	print "\n";
	print "wpupdater [path/to/plugin dir] [plugin name]\n";
	print "\nBoth arguments are optional\n\n";
}

sub argparser
{
	foreach (@ARGV) {
		$argu=$_;
		#print "Argument:$argu";
		if ($argu eq "--help")
		{
			&print_help;
			exit;
		}
	}

}

You can add these as a cron job.


You are reading this post on Joel G Mathew’s tech blog. Joel's personal blog is the Eyrie, hosted here.