I have a CGI perl script called install-app-pl.cgi:
#!/usr/bin/perl -w
print header('text/html');
use strict;
use CGI ':standard';
# Get me some vars
my #params = param();
my $APP_NAME = param('app_name');
my $APP_WEB_PORT = param('app_web_port');
my $APP_WEB_USER = param('app_web_user');
my $APP_WEB_PASS = param('app_web_pass');
my $DOWNLOAD_DIR = param('download_dir');
my $CONFIG_DIR = param('config_dir');
my $LIBRARY_DIR = param('library_dir');
my $TEMP_DOWNLOAD_DIR = param('temp_download_dir');
# Run another script
if ( $APP_NAME ) {
print "Installing $APP_NAME...";
print "<pre>";
system ("perl /var/www/mysite.local/public_html/lib/$APP_NAME/install-$APP_NAME.pl");
print "</pre>" ;
}
else {
print "No app specified, check the error log";
}
I'm trying to get it to pass the variables defined from the CGI parameters to install-$APP_NAME.pl
#!/usr/bin/perl -w
print header('text/html');
use strict;
use CGI ':standard';
require "/var/www/mysite.local/public_html/cgi-bin/install-app-pl.cgi"
# Echo my vars
print "$CONFIG_DIR $DOWNLOAD_DIR $LIBRARY_DIR $PGID $PUID $TZ $APP_WEB_PORT";
But I'm not sure of the best way to pass those on.
Are you sure that install-app-pl.cgi is a CGI program? Are you sure that it's not just a Perl command-line program? I mean, I see how it's named, but it seems very strange to call a CGI program using system() like that.
And the difference is crucial here. CGI programs access their parameters in a different way command-line programs.
If it really is a CGI program, then you have a few options:
Make an HTTP request to it (using something from the LWP bundle of modules).
Use CGI.pm's debugging mechanism to call it the same way as you're currently calling it, but passing the CGI parameters like foo=xxx&bar=yyy&baz=zzz (see the DEBUGGING section of the CGI.pm documentation for details). This, of course, relies on the program using CGI.pm and it feels a bit hacky to me.
Ask yourself if the program really needs to be a CGI program if you're calling from another program using system(). And then decide to rewrite it as a command-line program. If you want both a CGI version and a command-line version, then you could move most of the code to a module which could be used by two thin wrappers which just extract the parameters.
A few other points about your code.
Perl 5.6 (released in 2000) introduced a use warnings pragma. Most people now use that in place of -w on the shebang line.
It seems weird to call the header() function before loading the CGI module that defines it. It works, because the use is handled at compile time, but it would be nice to re-order that code to make more sense.
Similarly. most people would have use strict (and use warnings) as the very first things in their program. Immediately after the shebang line.
system() returns the return value from the process. If your second program produces useful output that you want displayed on the web page, you should use backticks instead.
If all of your output is going to be in a <pre> element, why not just remove that element and return a content type of "text/plain" instead?
Update: And I'd be remiss if I didn't reiterate what many people have already said in comments on your original question - this sounds like a terrible idea.
Related
I have two Perl scripts. Let's call them one.pl and two.pl.
one.pl processes some data and needs to call two.pl, passing a variable.
In one.pl I can
require "two.pl"
to call the second script.
That works fine, but I want to pass a CGI variable to two.pl.
Is there any way to do this without rewriting two.pl as a Perl module?
For example what I want is:
one.pl
...
require "two.pl $number";
two.pl
use CGI;
my $cgi = new CGI;
my $number = $cgi->param('number');
...
EDIT: two.pl should only ever be called once
If one.pl is not in a CGI environment
If your one.pl is a shell script, you can set #ARGV before the call to require. This abuses CGI's mode to work with command line arguments. The arg needs to be the param name equals the value.
{
my $number = 5;
local #ARGV = ( "number=$number" );
require "two.pl";
}
The key=value format is important. The local keyword makes sure that #ARGV is only set inside the block, making sure other possible arguments to your script are not permanently lost, but rather invisible to two.pl.
If one.pl is in a CGI environment
If the param is already there, you don't have to do anything.
Else, see above.
Note that for both of these you can only ever require a script once. That's the idea of require. Perl keeps track of what it's loaded already (in %INC). If you are in an environment like mod_perl, or a modern Perl application that runs persistently, you should use do "two.pl" instead, which will execute it every time. But then that might break other things if two.pl is not designed to be ran multiple times in the same process.
Your best bet is to refactor the code inside two.pl into a module, and use that in both scripts.
I am trying to get URL parameters using CGI and FCGI.
When executing the script on my browser I am only getting parameters of my first request, but the request $count does increment.
use CGI;
use FCGI;
my $fcgiRequest = FCGI::Request();
while ($fcgiRequest->Accept() >= 0)
{
print("Content-type: text/html\r\n\r\n", ++$count);
my $q = new CGI;
print "name = " . $q->param("name") ;
print "\n";
}
You can't use CGI.pm and FCGI.pm together like this. You need some glue-code in between, because CGI needs to read stuff from the environment, and it can't in the way you invoke it.
There is a module CGI::Fast included with Perl that does this for you. Instead of using CGI and FCGI, you just use CGI::Fast and change a bit of code.
use strict;
use warnings;
use CGI::Fast;
my $count;
while (my $q = CGI::Fast->new) {
# $q is a fresh CGI object for the current request
print("Content-type: text/html\r\n\r\n", ++$count);
print "name = " . $q->param("name") ;
print "\n";
}
If you already have a CGI script, you could put that in a function and call it inside of that loop.
If you're interested in how CGI::Fast works, feel free to take a look at its source code on CPAN. The main thing is that the environment and the file handles (for STDOUT and STDERR) need to be mapped to each new request, while your implementation has CGI always parse the first one.
Please note that the examples in both the perldoc and on CPAN that I have linked are very out-of-date and don't represent modern Perl code.
Today, you would probably not use CGI any more, as it has been removed from current Perl releases. Instead, you could take a look at the PSGI interface specification and Plack on how to implement it. The good thing about that is that it provides a clear interface across different deployment-technologies (such as CGI and Fast CGI). You can just let your program run as a simple CGI with Apache or nginx, or as a persistent FCGI process with Apache or other webservers, or as a standalone application through various Plack webservers that are written in Perl and are tailored for specific purposes.
Once a CGI.pm object is created for a request, it's reused there on out.
You can reset CGI.pm by adding the following to the end of the loop:
CGI::_reset_globals();
I have the following cgi bin script:
#! /usr/bin/perl
#
use utf8;
use CGI;
my $q = CGI->new();
my %params = $q->Vars;
print $q->header('text/html');
$w = $params{"words"};
print "$w\n";
I want to be able to call it as cgi-bin/script.pl?words=É for example, but when I do that, what's printed is not UTF-8, but instead garbled:
É
Is there any way to use cgi-bin with utf8?
Your line use utf8 doesn't do anything for you, other than allowing UTF-8 characters in the source file itself. You must make sure that the output handles (on STDOUT as well as any files) are set to utf8. One easy way to handle this is the utf8::all module. Also, make sure you are sending the correct headers, and use the -utf8 CGI pragma to treat incoming parameters as UTF-8. Finally, as always, be sure to use strict and warnings.
The following should get you started:
#!/usr/bin/perl
use strict;
use warnings;
use utf8::all;
use CGI qw(-utf8);
my $q = CGI->new;
print $q->header("text/html;charset=UTF-8");
print $q->param("words");
exit;
I have been having this problem of intermittent failure of utf8 encoding with my CGI script.
I tried everything but couldn't reliably repeat the problem.
I finally discovered that is is absolutely critical to be consistent with you use of the utf8 pragma throughout every module that uses CGI
use CGI qw(-utf8);
What seems to happen is that modperl invokes the CGI module just once per requests. If there is inconsistent including of the CGI module - say for some utility function that is just using a redirect function and you haven't bothered to set the utf8 pragma. Then this invocation can be the one that modperl decides to use to decode requests.
You will save yourself a lot of pain in the long run if you start out by reading the perlunitut and perlunicode documentation pages. They will give you the basics on exactly what Unicode and character encodings are, and how to work with them in Perl.
Also, what you're asking for is more complex than you think. There are many layers hidden in the phrase "use cgi-bin with utf8", starting with your interface to whatever tool you're using to send requests to the web server and ending with that tool having parsed a response and presenting it to you. You need to understand all those layers well enough to at least be able to tell if the problem lies in your CGI script or not. For example, it doesn't help if your script works perfectly if the problem is that bash and curl don't agree on the encoding of your command line arguments.
I have two perl scripts that I need to run together.
The first script defines a number of common functions and a main method.
Script 1(one.pl) Example:
#!/usr/bin/perl
sub getInformation
{
my $serverMode = $_[0];
my $port = $_[1];
return "You\nneed\nto\nparse\nme\nright\nnow\n!\n";
}
#main
&parseInformation(&getInformation($ARGV[0], $ARGV[1]));
The second is a script that calls the second script after defining two functions.
Script 2(two.pl) Example:
#!/usr/bin/perl
sub parseInformation
{
my $parsedInformation = $_[0];
#omitted
print "$parsedInformation";
}
my $parameterOne = $ARGV[0];
my $parameterTwo = $ARGV[1];
do "./one.pl $parameterOne $parameterTwo";
Command line usage:
> ./two.pl bayside 20
I have attempted to do this and the script seems to run however, whenever I run the script in perl -d two.pl mode I get no information from the debugger about the other script.
I have done some research and read about system, capture, require and do. If use the system function to run the script, how will I be able to export the functions defined in script two?
Questions:
1. Is there anyway to do this in perl?
2. If so how exactly do I need to achieve that?
I fully understand that perl is perl. Not another programming language. Unfortunately, when transitioning one tends to bring with what they knew with them. My apologies.
References:
How to run a per script from within a perl script
Perl documentation for require function
Generally speaking, that is not the way you should write reusable common functions in Perl. Instead, you should put the bulk of your code into Perl modules, and write just short scripts that act as wrappers for the modules. These short scripts should basically just grab and validate command-line arguments, pass those arguments to the modules for the real work, then format and output the results.
I really wish I could recommend perldoc perlmod to learn about writing modules, but it seems to mostly concentrate on the minutiae rather than a high-level overview of how to write and use a Perl module. Gabor Szabo's tutorial is perhaps a better place to start.
Here's a simple example, creating a script that outputs the Unix timestamp. This is the module:
# This file is called "lib/MyLib/DateTime.pm"
use strict;
use warnings;
package MyLib::DateTime;
use parent "Exporter";
our #EXPORT_OK = qw( get_timestamp );
sub get_timestamp {
my $ts = time;
return $ts;
}
1;
And this is the script that uses it:
#!/usr/bin/env perl
use strict;
use warnings;
use lib "/path/to/lib"; # path to the module we want, but
# excluding the "MyLib/DateTime.pm" part
use MyLib::DateTime qw( get_timestamp ); # import the function we want
# Here we might deal with input; e.g. #ARGV
# but as get_timestamp doesn't need any input, we don't
# have anything to do.
# Here we'll call the function we defined in the module.
my $result = get_timestamp();
# And here we'll do the output
print $result, "\n";
Now, running the script should output the current Unix timestamp. Another script that was doing something more complex with timestamps could also use MyLib::DateTime.
More importantly, another module which needed to do something with timestamps could use MyLib::DateTime. Putting logic into modules, and having those modules use each other is really the essence of CPAN. I've been demonstrating a really basic date and time library, but the king of datetime manipulation is the DateTime module on CPAN. This in turn uses DateTime::TimeZone.
The ease of re-using code, and the availability of a large repository of free, well-tested, and (mostly) well-documented modules on CPAN, is one of the key selling points of Perl.
Exactly.
Running 2 separate scripts at the same time won't give either script access to the others functions at all. They are 2 completely separate processes. You need to use modules. The point of modules is so that you don't repeat yourself, often called "dry" programming. A simple rule of thumb is:
If you are going to use a block of code more than once put it into a subroutine in the current script.
If you are going to use the same block in several programs put it in a module.
Also remember that common problems usually have a module on CPAN
That should be enough to get you going. Then if you're going to do much Perl Programming you should buy the book "Programming Perl" by Larry Wall, if you've programmed in other languages, or "Learning Perl" by Randal Schwartz if you're new to programming. I'm old fashioned so I have both books in print but you can still get them as ebooks. Also check out Perl.org as you're not alone.
I have a script that I wrote that can either be used on the command line or as a CGI script, and need to determine how the script was called so I can output a content-type header for web requests (and maybe some anti-cache headers too). My first thought is to check for the existance of http environment variables:
my $js = build_javascript();
if ( exists $ENV{HTTP_HOST} ) {
print "Content-type: text/javascript\n\n";
}
print $js;
Is there a better way?
According to the CGI specification in RFC3875 (section 4.1.4.), the GATEWAY_INTERFACE environment variable would be the authoritative thing to check whether you are running in a CGI context:
4.1.4. GATEWAY_INTERFACE
The GATEWAY_INTERFACE variable MUST be set to the dialect of CGI
being used by the server to communicate with the script.
There's really no way good way to tell if your script was started by a web server or from the command line. Any of the environment variables can be set in both situations. I often run CGI programs straight from the command line to test them, for instance.
Knowing that, if you want to pick one environment variable to use, it just has to be one that you won't set in the other situation, or one that you set in both but give different values to. In that case, choose any environment variable that you like.
If you want to get more sophisicated, you can use something like IO::Interactive to determine if you're connected to a terminal. If you aren't, the filehanandle that is_interactive returns is a null filehandle and the output goes nowhere:
print { is_interactive() } $http_header;
If you don't like how IO::Interactive decides, you can reimplement is_interactive. It's a very short piece of code and the higher-level interface is very nice.
I usually do a little trick at the beginning of my module:
exit run(#ARGV) unless caller(); # run directly if called from command line
sub run
{
process_options(#_);
...
}
sub process_options {
#ARGV = #_;
my %opts;
GetOptions(\%opts,
...
}
The module does not have to be named "run".