Monthly Archives: December 2010

Windows 7 Tips And Tricks: Keyboard Shortcuts

Foreword

The following describes the keyboard shortcuts available on the Windows 7 platform.

Overview

With the release of Windows 7 comes some pretty cool functionality, especially when it comes to keyboard shortcuts.  Some of the keyboard shortcuts have been around for a while.  Others are brand new and I’ll walk you through them.

Minimize All Windows

This command will minimize all currently open windows to your taskbar.  Performing this action again will restore all windows:

Win + M


Find

This command will bring up the Find window:

Win + F


Window Walker

This command will silently walk through all the open windows:

Win + T


Window Switch

This command will switch (isolate) through all open windows.  Add the “Shift” key to walk in reverse order.  Stay stationary on an item long enough and Windows will isolate that window:

Alt + Tab


Windows Mobility Center

This command will bring up the Windows Mobility Center:

Win + X


Windows Task Manager

This command will bring up the Windows Task Manager:

Ctrl+Shift+Esc


Show Desktop

This command will show the desktop removing all windows from view.  This command is very similar to Minimize All Windows.  Performing this action again will restore all windows.

Win + D


3D Window Peek

Win + Tab

Win + Tab

This command is new in Windows 7 and will allow you to show all your currently open windows in a 3D cascade.  Continue hitting the “tab” key to cycle forward through the windows.  Add the “shift” key and cycle through them in reverse:

Win + Tab


System Properties

This command brings up the System Properties dialog:

Win + Pause/Break


Lock Windows

This command locks Windows:

Win + L


Quick Display Change

This command sbrings up the quick display change dialog:

Win + P

Win + P

Win + P


Windows Explorer

This command brings up the Windows Explorer:

Win + E

Win + E

Win + E


Run Prompt

This command brings up the run prompt:

Win + R

Win + R

Win + R


Ease of Access Center

This command brings up the Ease of Access Center:

Win + U

Win + U

Win + U


Quick Window Control

These commands are new to Windows 7 and control how the window behaves in relation to your screen:

Win + Up Arrow (Maximizes Current Window)

Win + Down Arrow (Restores Current Window)

Win + Left Arrow (Fit window left half screen)

Win + Right Arrow (Fit window right half screen)

How To Convert M2TS To WMV For Free

Foreword

The following is a tutorial for how to convert M2TS files to WWMV files for free.  All the examples given are for use with Windows but should work with any operating system other than the binaries.

Overview

So after purchasing an extremely cool Panasonic Blueray/HD video camcorder, I realized that the format it stores directly from the import software is in M2TS format.  Which on its own is fine, but good luck trying to use that format with any other software, especially After Effects.  There are tons of software applications out there to do the conversion for you but they all cost, suck terribly or some combination of both.  I’ve found a way to do it for free on any platform.

Getting Started

First thing you’re going to need is the latest version of FFMPEG.  For windows get the unofficial night build from here:

http://ffmpeg.arrozcru.org/autobuilds/

Just click on the static “w32” link, download and unzip any location of your choice.

Converting

To convert the M2TS file to WMV, I created a simple batch file to use:

ffmpeg -i %1 -vcodec wmv2 -b 16000k -acodec wmav2 -ac 2 -ab 128k -ar 44100 -s 1920x1080 %1.avi

Just for reference if you’re unfamiliar with Windows batch, the “%1” is the first argument passed to the batch command.  You may need to change the following arguments to match your camera’s capture settings:

The “-s” argument is the source dimension of the video.
The “-ab” argument is the audio bit rate.
The “-b” argument is the video bit rate.

Save this file and call it whatever you want (ie. convert.bat).  Once you’ve adjusted the arguments to fit your camera settings execute the command using the following:

user@computer:$ convert.bat myvideo.m2ts

That’s all there is to it. Let me know if you have any problems. Enjoy!

jQuery Quick Start Tutorial

Foreword

The following is a quick crash course in using jQuery.  If you’ve never used jQuery before and want to know to how without reading a ton, this is the tutorial for you.

Getting Started

First download the latest version and include it in a page.  Grab the minified version for speed or if you want to read through the code yourself, grab the uncompressed version.

http://docs.jquery.com/Downloading_jQuery#Download_jQuery

Next simply include the library in your HTML page just like any other JavaScript file.

<script type="text/javascript" src="script/jquery-1.4.4.min.js"></script>

Now we’re ready to start using it.

Dom Traversal

So how do we select a node or nodes.  We have a few different choices, we can use CSS selector syntax, or we can use xpath syntax.  Let’s try an example on the following markup.

<html>
<body>
<div class="container">
<span  id="title">Hello World</span>
</div>
</body>
</html>

To apply a standard CSS selector we could use something like “.container #title{ style }” or just “#title{ style }”.  Same with jQuery, here’s an example:

<script type="text/javascript">
 
$(function(){
    var titleSpan = $(".container #title");
    //title span is now set to the span title node
    titleSpan = $("#title");
    //this does the same as well. 
}); 
 
</script>

Now if you’re unfamiliar with jQuery, the above syntax may look a bit odd but here’s the breakdown.  “$” is an alias of the jQuery object, which is what you’ll use to interact with all jQuery APIs.  The long hand for of the above would look like this:

<script type="text/javascript">
 
$(document).ready(function(){
    ...
});
 
</script>

Both do the same thing, wait until the document has fully loaded, then process the function passed into the ready event.  There are several methods for traversing the DOM.  To view the entire list checkout:

http://api.jquery.com/category/traversing/

DOM Manipulation

So how do we work with the DOM, change it’s contents, etc.  jQuery makes this really easy, and here’s how.  Let’s say we wanted to get the text content of the title node using the following HTML.

<html>
<body>
<div class="container">
<span id="title">Hello World</span>
</div>
</body>
</html>

Here’s all we need to do.

var titleText = $("#title").text();

Boom! Done!  Very easy.  But what about changing the text contents of the node. Again:

$("#title").text("Hello Dolly");

Pie!  So if you pass an argument to the text method, it sets the content, without an argument, it retrieves it.  What if we needed to get the innerHTML value of a node.  Here’s what we do:

var html = $(".container").html();

That’s all there is to it, this example grabs the “container” class and retrieves the html content of the node.  What about setting it you ask? Check it out:

$(".container").html('<span id="footer">Goodbye</span>');

That’s all there is to it.  For more information on DOM manipulation, have a look at:

http://api.jquery.com/category/manipulation/

Working With Events

So now it’s time to be productive.  Let’s do something semi-based in the real world.  Let’s say we have a list of anchors that we want to attach a click event to.  Now the old school approach would be to do something like this:

var els = document.getElementsByTagName("a");
int len = els.length
for(var i=0; i<len; i++){
    if(els[i].className == ".showImage"){
        els[i].onclick = function(event){
            ...
        }
    }
}

This approach would allow us to get all the anchor elements off the page, then iterate each to find a match for class “showImage”, then bind a click event to that node and continue.  Using jQuery the same thing can be done if far less code:

$("a.showImage").click(function(){
    ...
});

Quite a difference, but both perform the same functionality.  In jQuery a selector can return a single or an array of elements depending on the markup and selector syntax used.  If the above looks a little confusing, consider this example:

$("a.showImage").each(function(){
    //this will return all a.showImage and call this each function on a per returned node basis
    //basically, this is a for each
 
    var anchor = $(this); //returns the current iteration object of each
 
    anchor.click(function(){
        ...
    });
});

The above does the same as the example before it.  Difference is the amount of code written and for demonstration purposes you can hopefully better understand what’s going on behind the scenes.

So now we now to to bind events, but what about unbinding events.  This example should help:

$("a.showImage").each(function(){
    var anchor = $(this);
    //bind the click event to the anchor
    anchor.bind("click", function(){
        ...
    });
    //unbind the click event from the anchor
    anchor.unbind("click");
});

When you use “object.click”, it’s the same as calling the “bind” method of the object.  Therefore to remove the “bind”, simply “unbind”.  For more information on handling jQuery events see:

http://api.jquery.com/category/events/

Conclusion

That’s about the gist of it.  The rest you should be able to discover on your own.  If you have any questions or find anything I’ve mentioned here to be confusing, please feel free to ask in the comments.

Creating RightScale Attachment Scripts

Foreword

The following is a primer on creating RightScale scripts that execute with attachments.

Overview

Creating RightScale scripts can be a little daunting at first, but with a little time and effort one can quickly ramp up from beginner to expert.  The examples show here will be executed on a Linux CentOS instance.

Basic Script

So let’s go over some RightScale script basics.  First let’s start by creating a new script.  Click the Design->RightScripts menu option.

Design -> RightScripts

Design -> RightScripts

Next click the “New” button.

New Button

New Button

Now you should be looking at the “New RightScript” screen.  Here we will give the script a name, enter a description, identify input values and write the actual script to be executed on the client machine.

New RightScript Screen

New RightScript Screen

Let’s begin by entering a name and description for the RightScript.  Enter any name and description you want.

It’s important to note here, that changing the RightScript name, will not affect any instance you have the scripts attached to.  In other words, once you attach a script to an instance, if you decide to rename that script later, feel free to do so with no consequences.  The next time you see the script on the instance, it will have the updated name, there’s no need to go back and re-add it.

Inputs will be used to dynamically get values entered from the user or from a predefined value.  I’ll explain this more in detail in a little while.  First let’s create a simple script.  Let’s create a simple script that copies a file from one location to the other.  Enter the following into the script field:

cp /tmp/file1 /tmp/file2

Now I know that this is extremely simple, but we’ll build from this.  So the script when executed get’s run against the default interpreter, in this instance BASH, and it copies a file that does not exist called “file1” to the same directory and names it “file2”.  Pretty useless, but now let’s make it a bit more useful.  Let’s say we wanted to have a utility to copy any file from one location to another from within the RightScale interface.  Let’s modify the script to do so.  Change the script to the following:

cp $FROM_LOCATION $TO_LOCATION

Now click the “Identify” button located just above the script field.  Did you see what happened?  RightScale realizes that the variables “$FROM_LOCATION” and “$TO_LOCATION” are not defined and offers them as input values to be input by the user and the time of execution.  In other words, when you decide to execute this script, it will prompt the user for the value of the “$FROM_LOCATION” variable and the “$TO_LOCATION” variable.

By default, the input types are “Any”, meaning any free text value.  You can if you wish change the type to be a dropdown and set a distinct set of values from the user to choose from.  But for this example let’s leave it as any.

Now this example is a bit more useful.  We can now pretty much move any file from one location to another through the RightScale interface.  But what about if we wanted to have a file uploaded and then copied out to a directory.  Let’s try that shall we?  First let’s modify the script a bit to use an uploaded attachment:

Attachment Script

cp $ATTACH_DIR/* $TO_LOCATION

Next let’s hit the “Identify” button again.  Notice, the “$FROM_LOCATION” input field disappears because it is no longer needed, but where is the “$ATTACH_DIR” input variable.  The “$ATTACH_DIR” input variable is defined by RightScale to allow you to access any attachments you decide to upload to the script.  Let’s try this and see how it works.

Let’s first enter a description for the “$TO_LOCATION” field we’ve added.  This will allow users to understand what the input field is to be used for.  For now enter this in the “TO_LOCATION” description field:

“Enter the destination for the attached file. (ie. /tmp)”

Then click the “Save” button.  You should now be presented with the tabbed interface to your script.

Scripts

Scripts

Next click the “Attachments” tab.

Attachments

Attachments

Here we will click the “Choose File” button and upload a file attachment to our script.  Create a simple text file called “test.txt”, click “Choose File”, select the newly created file “test.txt” and finally click “Upload”.  You should then see the file listed about the “Upload file” area.

Attachment Uploaded

Attachment Uploaded

Finally let’s try running the script to see how it works.

Executing The Script

To execute the script, we’ll need to select a running instance to have the script act upon.  First let’s get our deployments and select and instance.  Do this by selecting Manage->Deployments.

Manage -> Deployments

Manage -> Deployments

Then click on the server template you want to execute the script on from the list of servers.  Now click on the “Scripts” tab for your selected server template.

Template Scripts

Template Scripts

Next click the “Add Script” button under the “Operational Scripts” area.

Select A Script Dialog

Select A Script Dialog

You should now be presented with the “Select a script” dialog.  Click “Unpublished” and click on your newly created script, then click the “Select” button.  This action will attach the script to the server template and make it executable on running instances built off this template.  To execute the script, Go back to your Manage->Deployments screen and select the actual running server, click the “Scripts” tab, then click the blue “Run” button located to the right of the script.

Once the “Run” button has been clicked, the “Queued”, “Status” and “Completed” status will appear in the top left status pane.  If all goes well, you’ll have a successfully created and run script.  You can verify this by clicking on the “Completed” status when it appears.  Or by opening a terminal into to instance and verifying the appearance of the test.txt file.

How To Cluster Rabbit-MQ

Foreword

This explanation of clustering Rabbit-MQ assumes that you’ve had some experience with Rabbit-MQ.  At least to the point of being able to get Rabbit-MQ up and running and processing messages.  For this explanation I will be using CentOS linux, other linux distributions may or may not require slight modifications to the setup process.  You will need at least 2 machines or virtual instances up and running and install Rabbit-MQ on both.

Overview

Clustering Rabbit-MQ is actually very simple once you understand what’s going on and how it actually works.  There is no need for a load balancer or any other hardware/software component and the idea is simple.  Send all messages to the master queue and let the master distribute the messages down to the slaves.

Create Short Names

First, we need to change the host name and host entries of our machines to something short.  Rabbit-MQ has trouble clustering queues will fully qualified DNS names.  We’ll need a single short word host and route.  For now, let’s use the names “master” for the master head, then “slave1”, “slave2” … “slaveN” respectively for the rest.

Set the master host name to “master”

user@computer:$ echo "master" > /proc/sys/kernel/hostname

Next we need to set the entries in the /etc/hosts file to allow the short names to be aliased to machine or instance IPs.  Open the /etc/hosts file in your favorite editor and add the following lines:

user@computer:$ cat /etc/hosts
127.0.0.1   localhost   localhost.localdomain

192.168.0.100 master master.localdomain
192.168.0.101 slave1 slave1.localdomain
192.168.0.102 slave2 slave2.localdomain

Please note: Your particular /etc/hosts file will look different that the above. You’ll need to substitute your actual ip and domain suffix for each entry.

Make sure each slave you plan to add has an entry in the /etc/hosts file of the master. To verify your settings for each of the entries you provide, try pinging them by their short name.

user@computer:$ ping master
PING master (192.168.0.100) 56(84) bytes of data.
64 bytes from master (192.168.0.100): icmp_seq=1 ttl=61 time=0.499 ms
64 bytes from master (192.168.0.100): icmp_seq=2 ttl=61 time=0.620 ms
64 bytes from master (192.168.0.100): icmp_seq=3 ttl=61 time=0.590 ms
64 bytes from master (192.168.0.100): icmp_seq=4 ttl=61 time=0.494 ms

If you get something like the above, you’re good to go. If not, take a good look at your settings and adjust them until you do.

Once your short names are setup in the master /etc/hosts file, copy the /etc/hosts file to every slave so that all machines have the same hosts file entries, or to be more specific, that each machine has the master and slave routes. If you’re familiar with routing, feel free to just add the missing routes.

Then for each slave update the host name.

user@computer:$ echo "slave1" > /proc/sys/kernel/hostname
user@computer:$ echo "slave2" > /proc/sys/kernel/hostname
user@computer:$ echo "slave3" > /proc/sys/kernel/hostname

Synchronize ERLang Cookie

Next we need to synchronize our ERlang cookie. Rabbit-MQ needs this to be the same on all machines for them to communicate properly. The file we need is located on the master at /var/lib/rabbitmq/.erlang.cookie, we’ll cat this value then update all the cookies on the slave.

user@computer:$ cat /var/lib/rabbitmq/.erlang.cookie
DQRRLCTUGOBCRFNPIABC

Copy the value displayed by the cat.

Please notice that the file itself is storing the value without a carriage return nor a line feed. This value needs to go into the slaves the same way. Do so be executing the following command on each slave. Make sure you use the “-n” flag.

First let’s make sure we stop the rabbitmq-server on the slaves before updating the ERlang cookie.

user@computer:$ service rabbitmq-server stop
user@computer:$ service rabbitmq-server stop
user@computer:$ service rabbitmq-server stop

Next let’s update the cookie and start the service back up.

user@computer:$ echo -n "DQRRLCTUGOBCRFNPIABC" > /var/lib/rabbitmq/.erlang.cookie
user@computer:$ service rabbitmq-server start
user@computer:$ echo -n "DQRRLCTUGOBCRFNPIABC" > /var/lib/rabbitmq/.erlang.cookie
user@computer:$ service rabbitmq-server start
user@computer:$ echo -n "DQRRLCTUGOBCRFNPIABC" > /var/lib/rabbitmq/.erlang.cookie
user@computer:$ service rabbitmq-server start

Once again substitute the “DQRRLCTUGOBCRFNPIABC” value with your actual ERlang cookie value.

Create The Cluster

Now we cluster the queues together. Starting with the master, issue the following commands:

user@computer:$ rabbitmqctl stop_app
user@computer:$ rabbitmqctl reset
user@computer:$ rabbitmqctl start_app

Next we cluster the slaves to the master. For each slave execute the following commands:

user@computer:$ rabbitmqctl stop_app
user@computer:$ rabbitmqctl reset
user@computer:$ rabbitmqctl cluster rabbit@master
user@computer:$ rabbitmqctl start_app

These commands actually do the clustering of the slaves to the master. To verify that everything is in working order issue the following command on any master or slave instance:

user@computer:$ rabbitmqctl status
Status of node rabbit@master ...
[{running_applications,[{rabbit,"RabbitMQ","1.7.2"},
{mnesia,"MNESIA CXC 138 12","4.4.3"},
{os_mon,"CPO CXC 138 46","2.1.6"},
{sasl,"SASL CXC 138 11","2.1.5.3"},
{stdlib,"ERTS CXC 138 10","1.15.3"},
{kernel,"ERTS CXC 138 10","2.12.3"}]},
{nodes,[rabbit@slave1,rabbit@slave2,rabbit@slave3,rabbit@master]},
{running_nodes,[rabbit@slave1,rabbit@slave2,rabbit@slave3,rabbit@master]}]
...done.

Notice the lines containing nodes and running_nodes. They should list out all of the mater and slave entries. If they do not, you may have done something wrong. Go back and try executing all the steps again. Otherwise, you’re good to go. Start sending messages to the master and watch as they are distributed to each of the slave nodes.

You can always dynamically add more slave nodes. To do this, updated the /etc/hosts file of all the machines with the new entry. Copy the master ERlang cookie value to the new slave. Execute the commands to cluster the slave to the master and verify.

Troubleshooting

If you accidentally update the cookie value before you’ve stopped the service, you could get strange errors the next time you start the rabbitmq-server. If this happens just issue the following command:

user@computer:$ rm /var/lib/rabbitmq/mnesia/rabbit/* -f

This removes the mnesia storage and allows you to restart the rabbitmq-server without errors.

Using Hadoop And PHP

Getting Started

So first things first.  If you haven’t used Hadoop before you’ll first need to download a Hadoop release and make sure you have Java and PHP installed.  To download Hadoop head over to:

http://hadoop.apache.org/common/releases.html

Click on download a release and choose a mirror.  I suggest choosing the most recent stable release.  Once you’ve downloaded Hadoop, unzip it.

user@computer:$ tar xpf hadoop-0.20.2.tar.gz

I like to create a symlink to the hadoop-<release> directory to make things easier to manage.

user@computer:$ link -s hadoop-0.20.2 hadoop

Now you should have everything you need to start creating a Hadoop PHP job.

Creating The Job

For this example I’m going to create a simple Map/Reduce job for Hadoop.  Let’s start by understanding what we want to happen.

  1. We want to read from an input system – this is our mapper
  2. We want to do something with what we’ve mapped – this is our reducer

At the root of your development directory, let’s create another directory called script.  This is where we’ll store our PHP mapper and reducer files.

user@computer:$ ls

.
..
hadoop-0.20.2
hadoop-0.20.2.tar.gz
hadoop
user@computer:$ mkdir script

Now let’s being creating our mapper script in PHP.  Go ahead and create a PHP file called mapper.php under the script directory.

user@computer:$ touch script/mapper.php

Now let’s look at the basic structure of a PHP mapper.

#!/usr/bin/php
<?php
//this can be anything from reading input from files, to retrieving database content, soap calls, etc.
//for this example I'm going to create a simple php associative array.
$a = array(
'first_name' => 'Hello',
'last_name' => 'World'
);
//it's important to note that anything you send to STDOUT will be written to the output specified by the mapper.
//it's also important to note, do not forget to end all output to STDOUT with a PHP_EOL, this will save you a lot of pain.
echo serialize($a), PHP_EOL;
?>

So this example is extremely simple.  Create a simple associative array and serialize it.  Now onto the reducer.  Create a PHP file in the script directory called reducer.php.

user@computer:$ touch script/reducer.php

Now let’s take a look at the layout of a reducer.

#!/usr/bin/php
 
<?php
 
//Remember when I said anything put out through STDOUT in our mapper would go to the reducer.
//Well, now we read from the STDIN to get the result of our mapper.
//iterate all lines of output from our mapper
while (($line = fgets(STDIN)) !== false) {
    //remove leading and trailing whitespace, just in case 🙂
    $line = trim($line);
    //now recreate the array we serialized in our mapper
    $a = unserialize($line);
    //Now, we do whatever we need to with the data.  Write it out again so another process can pick it up,
    //send it to the database, soap call, whatever.  In this example, just change it a little and
    //write it back out.
    $a['middle_name'] = 'Jason';
    //do not forget the PHP_EOL
    echo serialize($a), PHP_EOL;
}//end while
?>

So now we have a very simple mapper and reducer ready to go.

Execution

So now let’s run it and see what happens.  But first, a little prep work.  We need to specify the input directory that will be used when the job runs.

user@computer:$ mkdir input
user@computer:$ touch input/conf

Ok, that was difficult.  We have an input directory and we’ve created an empty conf file.  The empty conf file is just something that the mapper will use to get started.  For now, don’t worry about it.  Now let’s run this bad boy.  Make sure you have your JAVA_HOME set, this is usually in the /usr directory.  You can set this by running #export JAVA_HOME=/usr.

user@computer:$ hadoop/bin/hadoop jar hadoop/contrib/streaming/hadoop-0.20.2-streaming.jar -mapper script/mapper.php -reducer script/reducer.php -input input/* -output output

So here’s what the command does.  The first part executes the hadoop execute script.  The “jar” argument tells hadoop to use a jar, in this case it tells it to use “hadoop/contrib/streaming/hadoop-0.20.2-streaming.jar”.  Next we pass the mapper and reducer arguments to the job and specify input and output directories.  If we wanted to, we could pass configuration information to the mapper, or files, etc.  We would just use the same line read structure that we used in the reducer to get the information.  That’s what would go in the input directory if we needed it to.  But for this example, we’ll just pass nothing.  Next the output directory will contain the output of our reducer.  In this case if everything works out correct, it will contain the PHP serialized form of our modified $a array.  If all goes well you should see something like this:

user@computer:$ hadoop/bin/hadoop jar hadoop/contrib/streaming/hadoop-0.20.2-streaming.jar -mapper script/mapper.php -reducer script/reducer.php -input input/* -output output

10/12/10 12:53:56 INFO jvm.JvmMetrics: Initializing JVM Metrics with processName=JobTracker, sessionId=
10/12/10 12:53:56 WARN mapred.JobClient: No job jar file set.  User classes may not be found. See JobConf(Class) or JobConf#setJar(String).
10/12/10 12:53:56 INFO mapred.FileInputFormat: Total input paths to process : 1
10/12/10 12:53:56 INFO streaming.StreamJob: getLocalDirs(): [/tmp/hadoop-root/mapred/local]
10/12/10 12:53:56 INFO streaming.StreamJob: Running job: job_local_0001
10/12/10 12:53:56 INFO streaming.StreamJob: Job running in-process (local Hadoop)
10/12/10 12:53:56 INFO mapred.FileInputFormat: Total input paths to process : 1
10/12/10 12:53:56 INFO mapred.MapTask: numReduceTasks: 1
10/12/10 12:53:56 INFO mapred.MapTask: io.sort.mb = 100
10/12/10 12:53:57 INFO mapred.MapTask: data buffer = 79691776/99614720
10/12/10 12:53:57 INFO mapred.MapTask: record buffer = 262144/327680
10/12/10 12:53:57 INFO streaming.PipeMapRed: PipeMapRed exec [/root/./script/mapper.php]
10/12/10 12:53:57 INFO streaming.PipeMapRed: MRErrorThread done
10/12/10 12:53:57 INFO streaming.PipeMapRed: Records R/W=0/1
10/12/10 12:53:57 INFO streaming.PipeMapRed: MROutputThread done
10/12/10 12:53:57 INFO streaming.PipeMapRed: mapRedFinished
10/12/10 12:53:57 INFO mapred.MapTask: Starting flush of map output
10/12/10 12:53:57 INFO mapred.MapTask: Finished spill 0
10/12/10 12:53:57 INFO mapred.TaskRunner: Task:attempt_local_0001_m_000000_0 is done. And is in the process of commiting
10/12/10 12:53:57 INFO mapred.LocalJobRunner: Records R/W=0/1
10/12/10 12:53:57 INFO mapred.TaskRunner: Task 'attempt_local_0001_m_000000_0' done.
10/12/10 12:53:57 INFO mapred.LocalJobRunner:
10/12/10 12:53:57 INFO mapred.Merger: Merging 1 sorted segments
10/12/10 12:53:57 INFO mapred.Merger: Down to the last merge-pass, with 1 segments left of total size: 70 bytes
10/12/10 12:53:57 INFO mapred.LocalJobRunner:
10/12/10 12:53:57 INFO streaming.PipeMapRed: PipeMapRed exec [/root/./script/reducer.php]
10/12/10 12:53:57 INFO streaming.PipeMapRed: R/W/S=1/0/0 in:NA [rec/s] out:NA [rec/s]
10/12/10 12:53:57 INFO streaming.PipeMapRed: Records R/W=1/1
10/12/10 12:53:57 INFO streaming.PipeMapRed: MROutputThread done
10/12/10 12:53:57 INFO streaming.PipeMapRed: MRErrorThread done
10/12/10 12:53:57 INFO streaming.PipeMapRed: mapRedFinished
10/12/10 12:53:57 INFO mapred.TaskRunner: Task:attempt_local_0001_r_000000_0 is done. And is in the process of commiting
10/12/10 12:53:57 INFO mapred.LocalJobRunner:
10/12/10 12:53:57 INFO mapred.TaskRunner: Task attempt_local_0001_r_000000_0 is allowed to commit now
10/12/10 12:53:57 INFO mapred.FileOutputCommitter: Saved output of task 'attempt_local_0001_r_000000_0' to file:/root/output
10/12/10 12:53:57 INFO mapred.LocalJobRunner: Records R/W=1/1 > reduce
10/12/10 12:53:57 INFO mapred.TaskRunner: Task 'attempt_local_0001_r_000000_0' done.
10/12/10 12:53:57 INFO streaming.StreamJob:  map 100%  reduce 100%
10/12/10 12:53:57 INFO streaming.StreamJob: Job complete: job_local_0001
10/12/10 12:53:57 INFO streaming.StreamJob: Output: output

If you get errors where it’s complaining about the output directory, just remove the output directory and try again.

Result

Once you’ve got something similar to the above and no errors, we can check out the result.

user@computer:$ cat output/*

a:3:{s:10:"first_name";s:5:"Hello";s:9:"last_name";s:5:"World";s:11:"middle_name";s:5:"Jason";}

There we go, a serialized form of our modified PHP array $a.  That’s all there is to it.  Now, go forth and Hadoop.