<html><head><title>App::combinesheets</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" >
<style type="text/css">
<!--/*--><![CDATA[/*><!--*/
BODY {
background: white;
color: black;
font-family: arial,sans-serif;
margin: 0;
padding: 1ex;
}
A:link, A:visited {
background: transparent;
color: #006699;
}
A[href="#POD_ERRORS"] {
background: transparent;
color: #FF0000;
}
DIV {
border-width: 0;
}
DT {
margin-top: 1em;
margin-left: 1em;
}
.pod { margin-right: 20ex; }
.pod PRE {
background: #eeeeee;
border: 1px solid #888888;
color: black;
padding: 1em;
white-space: pre;
}
.pod H1 {
background: transparent;
color: #006699;
font-size: large;
}
.pod H1 A { text-decoration: none; }
.pod H2 A { text-decoration: none; }
.pod H3 A { text-decoration: none; }
.pod H4 A { text-decoration: none; }
.pod H2 {
background: transparent;
color: #006699;
font-size: medium;
}
.pod H3 {
background: transparent;
color: #006699;
font-size: medium;
font-style: italic;
}
.pod H4 {
background: transparent;
color: #006699;
font-size: medium;
font-weight: normal;
}
.pod IMG {
vertical-align: top;
}
.pod .toc A {
text-decoration: none;
}
.pod .toc LI {
line-height: 1.2em;
list-style-type: none;
}
/*]]>*/-->
</style>
</head>
<body class='pod'>
<!--
generated by Pod::Simple::HTML v3.20,
using Pod::Simple::PullParser v3.20,
under Perl v5.012004 at Thu Mar 22 17:14:10 2012 GMT.
If you want to change this HTML document, you probably shouldn't do that
by changing it directly. Instead, see about changing the calling options
to Pod::Simple::HTML, and/or subclassing Pod::Simple::HTML,
then reconverting this document from the Pod source.
When in doubt, email the author of Pod::Simple::HTML for advice.
See 'perldoc Pod::Simple::HTML' for more info.
-->
<!-- start doc -->
<a name='___top' class='dummyTopAnchor' ></a>
<div class='indexgroup'>
<ul class='indexList indexList1'>
<li class='indexItem indexItem1'><a href='#NAME'>NAME</a>
<li class='indexItem indexItem1'><a href='#VERSION'>VERSION</a>
<li class='indexItem indexItem1'><a href='#SYNOPSIS'>SYNOPSIS</a>
<li class='indexItem indexItem1'><a href='#DESCRIPTION'>DESCRIPTION</a>
<ul class='indexList indexList2'>
<li class='indexItem indexItem2'><a href='#Duplicated_values_in_the_matching_columns'>Duplicated values in the matching columns</a>
</ul>
<li class='indexItem indexItem1'><a href='#ADVANCED_USAGE'>ADVANCED USAGE</a>
<ul class='indexList indexList2'>
<li class='indexItem indexItem2'><a href='#Calculated_columns_by_external_programs'>Calculated columns by external programs</a>
<li class='indexItem indexItem2'><a href='#Calculated_columns_by_a_Perl_subroutine'>Calculated columns by a Perl subroutine</a>
</ul>
<li class='indexItem indexItem1'><a href='#ARGUMENTS_and_OPTIONS'>ARGUMENTS and OPTIONS</a>
<li class='indexItem indexItem1'><a href='#ENVIRONMENT_VARIABLES'>ENVIRONMENT VARIABLES</a>
<ul class='indexList indexList2'>
<ul class='indexList indexList3'>
<li class='indexItem indexItem3'><a href='#COMBINE_SHEETS_EXT_PATH'>COMBINE_SHEETS_EXT_PATH</a>
</ul>
</ul>
<li class='indexItem indexItem1'><a href='#DEPENDENCIES'>DEPENDENCIES</a>
<li class='indexItem indexItem1'><a href='#KNOWN_BUGS%2C_MISSING_FEATURES'>KNOWN BUGS, MISSING FEATURES</a>
<li class='indexItem indexItem1'><a href='#SUPPORT'>SUPPORT</a>
<li class='indexItem indexItem1'><a href='#AUTHOR'>AUTHOR</a>
<li class='indexItem indexItem1'><a href='#COPYRIGHT_AND_LICENSE'>COPYRIGHT AND LICENSE</a>
</ul>
</div>
<h1><a class='u' href='#___top' title='click to go to top of document'
name="NAME"
>NAME</a></h1>
<p>App::combinesheets - command-line tool merging CSV and TSV spreadsheets</p>
<h1><a class='u' href='#___top' title='click to go to top of document'
name="VERSION"
>VERSION</a></h1>
<p>version 0.2.12</p>
<h1><a class='u' href='#___top' title='click to go to top of document'
name="SYNOPSIS"
>SYNOPSIS</a></h1>
<pre> combinesheets -h
combinesheets -help
combinesheets -man
combinesheets -version
combinesheets -config <config-file> -inputs <input-files> [<options>] [-outfile <output-file>]
where <input-files> has the form: <input-ID>=<filename> [<input-ID>=<filename>...]
where <options> are: -check</pre>
<h1><a class='u' href='#___top' title='click to go to top of document'
name="DESCRIPTION"
>DESCRIPTION</a></h1>
<p><b>combinesheets</b> is a command-line tool merging together two or more spreadsheets. The spreadsheets can be COMMA-separated or TAB-separated files, each of them having the first line with column headers. Data in one of the column (it can be a different column in each input spreadsheet) serve to match lines. For example, having two spreadsheets, PERSON and CAR, with the following contents:</p>
<pre> persons.tsv:
Surname First name Sex Age Nickname
Novak Jan M 52 Honza
Gudernova Jitka F 56
Senger Martin M 61 Tulak
cars.tsv:
Model Year Owned by
Praga 1936 Someone else
Mini 1968 Gudernova
Skoda 2002 Senger</pre>
<p>we want to merge these spreadsheet by <code>Surname</code> in persons.tsv and by <code>Owned by</code> in cars.tsv. There are two possible results, depending which spreadsheet is used as the first one (a primary one). If the persons.tsv is the first, the result will be (which columns are included in the result will be described later in this document):</p>
<pre> combinesheets -cfg config.cfg -in PERSON=persons.tsv CAR=cars.csv
First name Surname Model Sex Nickname Age Year Owned by
Jitka Gudernova Mini F 56 1968 Gudernova
Jan Novak M Honza 52
Martin Senger Skoda M Tulak 61 2002 Senger</pre>
<p>Or, if the cars.tsv is the first, the result will be:</p>
<pre> combinesheets -cfg config.cfg -in CAR=cars.csv PERSON=persons.tsv
First name Surname Model Sex Nickname Age Year Owned by
Jitka Gudernova Mini F 56 1968 Gudernova
Martin Senger Skoda M Tulak 61 2002 Senger
Praga 1936 Someone else</pre>
<p>Of course, if both input spreadsheets have only the matching lines, both results will be the same (it will not matter which one of them is considered the primary one).</p>
<p>The rows in the resulting spreadsheet are sorted by values in the column that was used as a matching column in the primary input.</p>
<p>The information which columns should be used to match the input spreadsheets and which columns should appear in the resulting spreadsheet is read from a configuration file (see the <code>-config</code> - or <code>-cfg</code> - argument).</p>
<p>The command-line arguments and options can be specified with single or double dash. Most of them can be abbreviated to the nearest non-biased length. They are case-sensitive.</p>
<h2><a class='u' href='#___top' title='click to go to top of document'
name="Duplicated_values_in_the_matching_columns"
>Duplicated values in the matching columns</a></h2>
<p>If there are repeated (the same) values in the column that serves as matching criterion then the resulting spreadsheet will have as many output lines (for a particular matching value) as is the number of all combinations of the lines with that matching values in all input spreadsheets. For example, let's have <code>books.tsv</code> and <code>authors.tsv</code>, assuming that a book can have more authors and any author can contribute to any number of books:</p>
<pre> books.tsv:
Title Note Author
Book 1 from B1-a Kim
Book 2 from B2-b Kim
Book 3 from B3-c Katrin
Book 1 from B1-d Blanka
Book 2 from B2-e Katrin
authors.tsv:
Age Name
28 Kim
20 Katrin
30 Blanka
50 Lazy author</pre>
<p>The output (again, depending on which input is considered a primary input) will be (a list of included column is defined in the configuration file - see later):</p>
<pre> combinesheets -cfg books_to_authors.cfg -in BOOK=books.tsv AUTHOR=authors.tsv
Name Title Age Note
Blanka Book 1 30 from B1-d
Katrin Book 3 20 from B3-c
Katrin Book 2 20 from B2-e
Kim Book 1 28 from B1-a
Kim Book 2 28 from B2-b
combinesheets -cfg books_to_authors.cfg -in AUTHOR=authors.tsv BOOK=books.tsv
Name Title Age Note
Blanka Book 1 30 from B1-d
Katrin Book 3 20 from B3-c
Katrin Book 2 20 from B2-e
Kim Book 1 28 from B1-a
Kim Book 2 28 from B2-b
Lazy author 50</pre>
<h1><a class='u' href='#___top' title='click to go to top of document'
name="ADVANCED_USAGE"
>ADVANCED USAGE</a></h1>
<p>Additionally to the merging columns from one or more spreadsheets, this script can also add completely new columns to the resulting spreadsheet, the columns that do not exist in any of the input spreadsheet. Such columns are called <code>calculated columns</code>.</p>
<p>Each <code>calculated column</code> is created either by an external, command-line driven, program, or by a Perl subroutine. In both cases, the user must create (write) such external program or such Perl subroutine. Therefore, this usage is meant more for developers than for the end users.</p>
<p>Note that this advanced feature is meant only for new columns, not for new rows. Therefore, it cannot be used, for example, to create rows with totals of columns.</p>
<h2><a class='u' href='#___top' title='click to go to top of document'
name="Calculated_columns_by_external_programs"
>Calculated columns by external programs</a></h2>
<p>If specified, an external program is invoked for each row. It can be specified either by a keyword <b>PROG</b> or by a keyword <b>PROGS</b> - see syntax in the <i>configuration</i> section. In both cases, the value of the standard output of these programs become the value of the calculated column (a trailing newline of this standard output is removed and other newlines are replaced by spaces).</p>
<p>A program defined by the <b>PROGS</b> is called without any arguments (<code>S</code> in <i>PROGS</i> stands for a <i>Simple</i>). That's why it does not have any knowledge for which row it has been invoked. Its usage is, therefore, for column values that are not dependent on other values from the spreadsheet. For example, for the <code>cars.tsv</code> shown above, you can add a column <code>Last updated</code> by calling a UNIX program <code>date</code> - again, see an example the <i>configuration</i> section.</p>
<p>A program defined by the <b>PROG</b> is called with one argument which is a filename. This file contains the current row; each of its lines has two, TAB-separated, fields. The first field is the column name and the second field is the column value. For example, when processing the last row of the <code>cars.tsv</code> given above, the file will have the following content:</p>
<pre> Model Skoda
Year 2002
Owned by Senger</pre>
<p>The files are only temporary and will be removed when <code>combinesheets</code> finishes.</p>
<h2><a class='u' href='#___top' title='click to go to top of document'
name="Calculated_columns_by_a_Perl_subroutine"
>Calculated columns by a Perl subroutine</a></h2>
<p>If specified by the keyword <b>PERL</b>, a Perl subroutine is called for each row with the three arguments:</p>
<ol>
<li>A hashref with information about the current column. Not often used but may be handy if the same subroutine deals with more columns and, therefore, needs to know for which column it was invoked. See the <i>flights</i> example in the <i>configuration</i> section.</li>
<li>An arrayref with all column names.</li>
<li>An arrayref with all column values - in the same order as the column names.</li>
</ol>
<p>Actually, depending how the subroutine is defined in the configuration, it may get as the first argument the module/class name where it belongs to. If you define it like this:</p>
<pre> PERL Module::Example::test</pre>
<p>the <code>test</code> subroutine is called, indeed, with the three arguments as described above. However, if your definition is rather:</p>
<pre> PERL Module::Example->test</pre>
<p>then the <code>test</code> subroutine is considered a Perl method and its first argument is the module/class name. It is up to you to decide how you want/need to write your functions. Again, an example is available in the <i>configuration</i> section.</p>
<p>The return value of the subroutine will become a new value in the calculated column. Do not return undef but rather an empty string if the value cannot be created.</p>
<p>What is an advantage of writing my own module/package if I can simply write an external program (perhaps also in Perl) doing exactly the same? The Perl module stays in the memory for the whole time of processing all input rows and, therefore, you can re-use some calculations done for the previous rows. An example about it (<code>flights</code>) is given in the <i>configuration</i> section.</p>
<h1><a class='u' href='#___top' title='click to go to top of document'
name="ARGUMENTS_and_OPTIONS"
>ARGUMENTS and OPTIONS</a></h1>
<dl>
<dt><a name="-config_<config-file>"
><b>-config <config-file</b>></a></dt>
<dd>
<p>A filename with a configuration file. This is a mandatory parameter. The configuration file describes:</p>
<ul>
<li>which columns in individual input spreadsheets should be included in the resulting spreadsheet,</li>
<li>what names should be given to the resulting columns</li>
<li>in which order should be the columns in the resulting spreadsheet</li>
<li>which columns should be used to match individual lines,</li>
</ul>
<p>The configuration file is a TAB-separated file (with no header line). Empty lines and lines starting with a "#" character are ignored. Each line has two columns, in some cases there is an optional third column. Here is a configuration file used in the example above:</p>
<pre> # Columns to match records from individual inputs
MATCH PERSON=Surname
MATCH CAR=Owned by
MATCH CHILD=Parent
# Columns - how they be in rows
PERSON First name
PERSON Surname
CAR Model
PERSON Sex
CHILD Name
CHILD Born
PERSON Nickname
PERSON Age
CAR Year
CAR Owned by</pre>
<p>The first column is either a reserved word <code>MATCH</code>, or an identifier of an input spreadsheet. There are also few other reserved words - see more about them a bit later.</p>
<p>The identifier can be almost anything (and it does not appear in the input spreadsheet itself). It is also used in the command-line argument <code>-inputs</code> where it corresponds to a real file name of the input. The lines with identifiers define what columns will be in the result: the second column is the header of the wanted columns and an optional third column (not used in the example above) is the header used in the result. The resulting columns will be in the same order as are these lines in the configuration file.</p>
<p>The reserved word <code>MATCH</code> is used to define how to match lines in the input spreadsheets. The format of its second column is:</p>
<pre> <input-ID>=<column-header></pre>
<p>There should be one MATCH line for each input spreadsheet. The data in the column defined by the "column-header" will be used to find the corresponding lines. In our example, the data in the column <i>Surname</i> in the <code>persons.tsv</code> will be matched with the data in the column <i>Owned by</i> in the <code>cars.tsv</code> (the rows having the same values in these two columns will be merged into one resulting row).</p>
<p><b>Advanced configuration</b></p>
<p>If you want to add so-called <i>calculated columns</i> as described in the <a href="#ADVANCED_USAGE" class="podlinkpod"
>"ADVANCED USAGE"</a> you need to use few additional reserved words in the configuration file. These words are <b>PROG</b>, <b>PROGS</b> and/or <b>PERL</b>. They are used in the place where the new calculated column should be placed. Their lines have the program name or the Perl subroutine name in the second column, and they have mandatory third column with the resulting name of the calculated column.</p>
<p>For example, we wish to add two columns to the input spreadsheet <code>cars.tsv</code>. The input file (the same as in the introduction) is:</p>
<pre> Model Year Owned by
Praga 1936 Someone else
Mini 1968 Gudernova
Skoda 2002 Senger</pre>
<p>We wish to add a column <i>Car age</i> that shows the difference between the actual year and the value from the <i>Year</i> column. We have a shell script <code>age.sh</code> doing it:</p>
<pre> #!/bin/bash
YEAR=`grep Year $1 | cut -f2`
NOW=`date +%Y`
echo $(($NOW-$YEAR))</pre>
<p>The configuration file <code>cars.cfg</code> (assuming that we want the other columns to remain the same) is:</p>
<pre> MATCH CAR=Owned by
CAR Owned by
CAR Model
CAR Year
PROG age.sh Car age</pre>
<p>When we run:</p>
<pre> combinesheets -config cars.cfg -inputs CAR=cars.tsv</pre>
<p>we get this result:</p>
<pre> Owned by Model Year Car age
Gudernova Mini 1968 44
Senger Skoda 2002 10
Someone else Praga 1936 76</pre>
<p>You can see that there is no need to use <code>combinesheets</code> for really combining <i>more</i> sheets, an input can be just one sheet.</p>
<p>Another example adds a <i>fixed</i> column to the same input, a column named <i>Last updated</i> that gets its value from a UNIX command <code>date</code>. This program does not get any information which row it has been invoked for. The configuration file is now (note the new line with the <b>PROGS</b>):</p>
<pre> MATCH CAR=Owned by
CAR Owned by
CAR Model
CAR Year
PROG age.sh Car age
PROGS date Last updated</pre>
<p>and the result is now:</p>
<pre> Owned by Model Year Car age Last updated
Gudernova Mini 1968 44 Mon Feb 27 12:32:04 AST 2012
Senger Skoda 2002 10 Mon Feb 27 12:32:04 AST 2012
Someone else Praga 1936 76 Mon Feb 27 12:32:04 AST 2012</pre>
<p>The last possibility is to call a Perl subroutine - using the reserved word <b>PERL</b> in the configuration file. Let's have an input spreadsheet (<code>flights.tsv</code>) with data about flights:</p>
<pre> Date Flight Airport From Airport To
2009-01-18 AY838 London LHR Helsinki Vantaa
2009-01-22 AY839 Helsinki Vantaa London LHR
2009-03-15 NW2 Manila Tokyo Narita
2009-03-21 NW1 Tokyo Narita Manila
2011-05-06 SV326 Sharm El Sheik Jeddah
2011-07-31 RJ700 Amman Jeddah
2011-09-21 ME369 Jeddah Beirut
2011-09-24 ME368 Beirut Jeddah
2011-12-02 EZY3064 Prague London Stansted
2011-12-09 EZY3067 London Stansted Prague
2012-01-26 MS663 Cairo Jeddah</pre>
<p>We want to add columns with the international airport codes for both <i>Airport From</i> and <i>Airport To</i>. The new columns will be named <i>Code From</i> and <i>Code To</i>. The Perl subroutine will use a web service to find the code. The subroutine will use a closure that will remember already fetched codes so the web service does not need to be called several times for the same airport name.</p>
<p>The configuration file <code>flights.cfg</code> is:</p>
<pre> MATCH FLY=Date
FLY Date
FLY Flight
FLY Airport From
PERL Airport->find_code Code From
FLY Airport To
PERL Airport->find_code Code To</pre>
<p>The name of the subroutine is attached to the module where it comes from by either <b>::</b> or <b>-></b> notation.</p>
<p>The invocation is:</p>
<pre> combinesheets -config flights.cfg -inputs FLY=flights.tsv</pre>
<p>The full code for the module <code>Airport</code>, the file <code>Airport.pm</code> is here:</p>
<pre> package Airport;
use warnings;
use strict;
use LWP::Simple;
use JSON;
# preparing a closure in order not to fetch the same airport code again and again
my $already_found = make_already_found();
sub make_already_found {
my $already_found = {};
return sub {
my ($airport_name, $airport_code) = @_;
if (exists $already_found->{$airport_name}) {
if ($airport_code) {
$already_found->{$airport_name} = $airport_code;
}
return $already_found->{$airport_name};
} else {
$already_found->{$airport_name} = ($airport_code ? $airport_code : 1);
return 0;
}
}
}
sub find_code {
my ($class, $column, $header_line, $data_line) = @_;
my $column_with_airport_name = $column->{ocol};
$column_with_airport_name =~ s{Code}{Airport};
my $airport_name;
for (my $i = 0; $i < @$header_line; $i++) {
if ($header_line->[$i] eq $column_with_airport_name) {
$airport_name = $data_line->[$i];
last;
}
}
return '' unless $airport_name;
# now we have an airport name...
my $airport_code = $already_found->($airport_name);
return $airport_code if $airport_code;
#... go and find its airport code
$airport_code = '';
my $escaped_airport_name = $airport_name;
$escaped_airport_name =~ tr{ }{+};
my $url = "http://airportcode.riobard.com/search?q=$escaped_airport_name&fmt=json";
my $content = get ($url);
warn "Cannot get a response for '$url'\n"
unless defined $content;
my $json = JSON->new->allow_nonref;
my $data = $json->decode ($content);
foreach my $code (@$data) {
$airport_code .= $code->{code} . ",";
}
chop ($airport_code) if $airport_code; # removing the trailing comma
$already_found->($airport_name, $airport_code);
return $airport_code;
}
1;</pre>
<p>When run it creates the following output. Note that some airports have more than one code because the name was ambiguous. Well, this is just an example, isn't it?</p>
<pre> Date Flight Airport From Code From Airport To Code To
2009-01-18 AY838 London LHR LHR Helsinki Vantaa HEL
2009-01-22 AY839 Helsinki Vantaa HEL London LHR LHR
2009-03-15 NW2 Manila MXA,MNL Tokyo Narita NRT
2009-03-21 NW1 Tokyo Narita NRT Manila MXA,MNL
2011-05-06 SV326 Sharm El Sheik SSH Jeddah JED
2011-07-31 RJ700 Amman ADJ,AMM Jeddah JED
2011-09-21 ME369 Jeddah JED Beirut BEY
2011-09-24 ME368 Beirut BEY Jeddah JED
2011-12-02 EZY3064 Prague PRG London Stansted STN
2011-12-09 EZY3067 London Stansted STN Prague PRG
2012-01-26 MS663 Cairo CAI,CIR Jeddah JED</pre>
<dt><a name="-inputs_<input_ID=<filename>_[<input_ID>=<filename>...]>"
><b>-inputs <input_ID</b>=<filename> [<input_ID>=<filename>...]></a></dt>
<dd>
<p>Each <code>-inputs</code> can have one or more file names, and there can be one or more <code>-inputs</code> arguments. It defines what are the input spreadsheets and how they are identified in the configuration file (see the <code>-config</code> argument). For example, the inputs for our example above can be specified in any of these ways:</p>
<pre> -inputs PERSON=persons.tsv -inputs CAR=cars.tsv
-inputs PERSON=persons.tsv CAR=cars.tsv
-inputs PERSON=persons.tsv,CAR=cars.tsv</pre>
<p>The first file name is considered to be the <code>primary</code> input (see the description above): the resulting spreadsheet will have the same number of lines as the primary input.</p>
<p>The file names ending with the <code>.csv</code> are considered to be in the COMMA-separated formats, all others are considered to be TAB-separated.</p>
<p>This is a mandatory parameter.</p>
<dt><a name="-outfile_<output-file>"
><b>-outfile <output-file</b>></a></dt>
<dd>
<p>An optional parameter specifying a filename of the combined result. By default, it is created on STDOUT. It is always in the TAB-separated format.</p>
<dt><a name="-check"
><b>-check</b></a></dt>
<dd>
<p>This option causes that the configuration file and the input files (only their header lines will be read) will be checked for errors but no resulting spreadsheet will be created.</p>
<dt><a name="-ignorecases"
><b>-ignorecases</b></a></dt>
<dd>
<p>Not yet implemented.</p>
<dt><a name="General_options"
><b>General options</b></a></dt>
<dd>
<dl>
<dt><a name="-h"
><b>-h</b></a></dt>
<dd>
<p>Print a brief usage message and exits.</p>
<dt><a name="-help"
><b>-help</b></a></dt>
<dd>
<p>Print a brief usage message with options and exit.</p>
<dt><a name="-man"
><b>-man</b></a></dt>
<dd>
<p>Print a full usage message and exit.</p>
<dt><a name="-version"
><b>-version</b></a></dt>
<dd>
<p>Print the version and exit.</p>
</dd>
</dl>
</dd>
</dl>
<h1><a class='u' href='#___top' title='click to go to top of document'
name="ENVIRONMENT_VARIABLES"
>ENVIRONMENT VARIABLES</a></h1>
<h3><a class='u' href='#___top' title='click to go to top of document'
name="COMBINE_SHEETS_EXT_PATH"
>COMBINE_SHEETS_EXT_PATH</a></h3>
<p>It contains a path that is used when looking for external programs (when the reserved words PROG or PROGS are used). For example, the <code>examples</code> directory in the source distribution of this package has an external program <code>age.sh</code>. The full invocation can be done by:</p>
<pre> COMBINE_SHEETS_EXT_PATH=examples bin/combinesheets -cfg examples/cars.cfg --inputs CAR=examples/cars.csv</pre>
<h1><a class='u' href='#___top' title='click to go to top of document'
name="DEPENDENCIES"
>DEPENDENCIES</a></h1>
<p>In order to run this tool you need Perl and the following Perl modules to be installed:</p>
<pre> App::Cmd::Simple
Text::CSV::Simple
Text::CSV_XS
File::BOM
Getopt::Long::Descriptive
Pod::Usage
Algorithm::Loops</pre>
<p>Optionally (if your configuration file uses the reserved word PROG or PROGS for calculated columns):</p>
<pre> IO::CaptureOutput</pre>
<h1><a class='u' href='#___top' title='click to go to top of document'
name="KNOWN_BUGS,_MISSING_FEATURES"
>KNOWN BUGS, MISSING FEATURES</a></h1>
<ul>
<li>Columns are identified by their header names. There is no way to identify them simply by their order (column number).</li>
<li>The input spreadsheet are read first into memory. Which may be a problem with really huge spreadsheets.</li>
<li>The inputs can be COMMA-separated or TAB-separated. It would be perhaps nice to allow also the Excel spreadsheets.</li>
<li>Comparing header names and rows is case-sensitive only. There is a plan to implement the option <code>-ignorecases</code>,</li>
</ul>
<p>Some of these missing features may be implemented later.</p>
<h1><a class='u' href='#___top' title='click to go to top of document'
name="SUPPORT"
>SUPPORT</a></h1>
<p>You can find documentation for this module with the perldoc command.</p>
<pre> perldoc App::combinesheets</pre>
<p>You can also look for information at:</p>
<ul>
<li>RT: CPAN's request tracker
<p><a href="http://rt.cpan.org/NoAuth/Bugs.html?Dist=App-combinesheets" class="podlinkurl"
>http://rt.cpan.org/NoAuth/Bugs.html?Dist=App-combinesheets</a></p>
</li>
<li>AnnoCPAN: Annotated CPAN documentation
<p><a href="http://annocpan.org/dist/App-combinesheets" class="podlinkurl"
>http://annocpan.org/dist/App-combinesheets</a></p>
</li>
<li>CPAN Ratings
<p><a href="http://cpanratings.perl.org/d/App-combinesheets" class="podlinkurl"
>http://cpanratings.perl.org/d/App-combinesheets</a></p>
</li>
<li>Search CPAN
<p><a href="http://search.cpan.org/dist/App-combinesheets/" class="podlinkurl"
>http://search.cpan.org/dist/App-combinesheets/</a></p>
</li>
</ul>
<h1><a class='u' href='#___top' title='click to go to top of document'
name="AUTHOR"
>AUTHOR</a></h1>
<p>Martin Senger <martin.senger@gmail.com></p>
<h1><a class='u' href='#___top' title='click to go to top of document'
name="COPYRIGHT_AND_LICENSE"
>COPYRIGHT AND LICENSE</a></h1>
<p>This software is copyright (c) 2012 by Martin Senger, CBRC - KAUST (Computational Biology Research Center - King Abdullah University of Science and Technology) All Rights Reserved..</p>
<p>This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.</p>
<!-- end doc -->
</body></html>