我想在CGI程序中运行shell命令(用Perl编写)。我的程序没有root权限。它像没人一样运行。我想使用这段代码:
use strict;
system <<'EEE';
awk '{a[$1]+=$2;b[$1]+=$3}END{for(i in a)print i, a[i], b[i]|"sort -nk 3"}' s.txt
EEE
我可以从命令行使用perl成功运行我的代码,但不能作为CGI程序运行。
答案 0 :(得分:1)
根据您问题中的代码,至少有四种失败的可能性。
#!
)行。您正在尝试运行awk
,因此我假设您正在运行某种形式的Unix。如果您的代码缺少此行,那么您的操作系统将不知道如何运行您的程序。s.txt
不在执行程序的工作目录中,或者无人用户无法读取。awk
。要快速诊断此类低级别问题,请尝试将所有错误输出显示在浏览器中。一种方法是在代码中的shebang行之后添加以下内容。
BEGIN {
print "Content-type: text/plain\n\n";
open STDERR, ">&", \*STDOUT or print "$0: dup: $!";
}
输出将呈现为纯文本而不是HTML,但这是查看程序输出的临时措施。通过将其包装在BEGIN
块中,代码在解析后立即执行。重定向STDERR
意味着您的浏览器也会获得写入标准输出的任何内容。
另一种方法是使用CGI::Carp模块。
use CGI::Carp 'fatalsToBrowser';
这样,错误会发送到浏览器以及Web服务器的错误日志。
如果您仍然看到来自服务器的500系列错误,则问题发生在较低级别:可能是某些启动失败perl
。去检查服务器的错误日志。程序执行后,您可以删除错误输出的临时重定向。
最后,我建议您将程序更改为
#! /usr/bin/perl -T
BEGIN { print "Content-type: text/plain\n\n"; }
use strict;
use warnings;
$ENV{PATH} = "/bin:/usr/bin";
my $input = "/path/to/your/s.txt";
my $buckets = <<'EOProgram'
{ a[$1] += $2; b[$1] += $3 }
END { for (i in a) print i, a[i], b[i] }
EOProgram
open STDIN, "-|", "awk", $buckets, $input or die "$0: open: $!";
exec "sort", "-nk", 3 or die "$0: exec: $!";
-T
开关启用名为taint mode的安全数据流分析,可防止您对系统操作(例如open
,exec
等)使用未经过抽取的输入攻击者(或提供意外输入的良性用户)可能会损害您的系统。您应该始终将-T
添加到CGI程序以及代表其他用户运行的任何其他代码。
鉴于你的awk程序的性质,text / plain的内容类型似乎是合理的。尽快输出。
启用污点模式后,请明确说明PATH环境变量的值。如果您坚持使用程序继承的任何不受信任的PATH,则尝试运行外部程序将失败。
指出输入的完整路径。这将消除意外。
使用open
和exec
的多参数形式消除了shell及其参数解析。 (为了完整性,system
也有类似的多参数形式。)是的,这样写它可能意味着being a little more deliberate(比如打破参数并自己设置管道),但它也是避免令人讨厌的惊喜。
答案 1 :(得分:0)
我确信允许nobody
运行shell命令。问题是nobody
无权打开文件s.txt
。将所有人的读取权限添加到s.txt
,并向每个目录中的所有人添加执行权限,直至s.txt
。
答案 2 :(得分:0)
我建议找出awk的完整限定路径并直接指定它。可能没有启动httpd的人在$ ENV {PATH}中有一条非常小的路径。显示我猜的$ ENV {PATH}将显示此信息。
这是一件好事,我不会修改路径,只是指定路径/ usr / bin / awk或者不是。
如果你有shell访问权限并且它有效,请输入'which awk'来查找它。
答案 3 :(得分:0)
我可以成功运行我的代码 perl文件但不在cgi文件中。
您在运行什么Web服务器?例如,apache需要打印CGI标题,即print "Content-type: text/plain; charset=utf-8\n\n"
或
use CGI;
my $q = CGI->new();
print $q->header('text/html');
(见CGI)
Apache会在日志(error.log
)中提及有关“脚本标题过早结束”的内容,如我所说的那样。
答案 4 :(得分:0)
你可以直接进行内联,而不必分叉到另一个进程......
if ( open my $fh, '<', 's.txt' ) {
my %data;
while (<$fh>) {
my ($c1,$c2,$c3) = split;
$data{a}{$c1} += $c2;
$data{b}{$c1} += $c3;
}
foreach ( sort { $data{b}{$a} <=> $data{b}{$b} } keys %{ $data{b} } ) {
print "$_ $data{a}{$_} $data{b}{$_}\n";
}
} else {
warn "Unable to open s.txt: $!\n";
}