我有一个shell脚本,相当大的一个。 现在我的老板说我必须在Perl重写。 有没有什么办法来写一个Perl脚本,并使用现有shell代码是在我的Perl脚本。 同样的事情也给内联中:C 。
有什么样内嵌::贝壳? 我看了一下内嵌模块,但它仅支持多种语言。
我有一个shell脚本,相当大的一个。 现在我的老板说我必须在Perl重写。 有没有什么办法来写一个Perl脚本,并使用现有shell代码是在我的Perl脚本。 同样的事情也给内联中:C 。
有什么样内嵌::贝壳? 我看了一下内嵌模块,但它仅支持多种语言。
我会认真地回答。 我不知道任何程序shell脚本翻译成Perl的,我怀疑任何解释器模块可以提供性能优势。 所以,我给的我怎么会去了解它的轮廓。
现在,你要尽可能地重用代码。 在这种情况下,我建议代码的选择件,写的是一个Perl的版本,然后从主脚本调用Perl脚本。 这将使你做一小步一小步的转换,断言,转换后的部分工作,并逐步提高你的Perl的知识。
你可以从一个Perl脚本中调用外部程序,你甚至可以代替用Perl一些大的逻辑,并调用从Perl的小shell脚本(或其他命令)做一些你感觉不舒服尚未转换。 所以,你将有一个shell脚本调用Perl脚本调用另一个shell脚本。 而且,事实上,我确实做到了我自己的第一个Perl脚本。
当然,选择好要转换什么是重要的。 我会解释,下面,有多少模式常见于shell脚本是用Perl编写,这样就可以通过尽可能多的识别它们的脚本中,并创造替代剪切和粘贴越好。
首先,无论是Perl脚本和shell脚本代码+功能。 即,这是不是一个函数声明在它遇到的顺序执行任何东西。 你不需要在使用前声明函数,虽然。 这意味着脚本的一般布局可以保留,但保留在内存中的东西(比如整个文件,或它的加工形式)的能力使得它能够简化任务。
一个Perl脚本,在Unix下,开始像这样的东西:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
#other libraries
(rest of the code)
第一行,显然,指出要用来运行脚本的命令,就像正常的炮弹做。 下面的两个“使用”线,然后让语言更加严格,这应该减少错误的你遇到的量,因为你不知道的语言以及(或纯做错了什么)。 第三个使用行导入“数据”模块的“翻车机”的功能。 这是用于调试。 如果你想知道一个数组或哈希表的价值,只打印自卸车(什么)。
还请注意,注释就像壳牌,开始以“#”线路。
现在,你可以调用外部程序,并从他们管或管。 例如:
open THIS, "cat $ARGV[0] |";
这将运行猫,通过“ $ARGV[0]
”,这将是对壳$ 1 -传递给它的第一个参数。 那结果将通过“本”,你可以用它来读取,从它,再告诉通过管道输送到你的Perl脚本。
你可以用“|” 在开始或行结束,指示模式“管”或“管道从”,并指定要运行的命令,你也可以使用“>”或“>>”开头,以打开文件为具有或不具有截断,“<”,以明确地指示打开一个文件,用于读取(缺省值),或者“+ <”和“+>”写入用于读取和写入。 请注意,以后将首先截断该文件。
另一个语法“打开”,这将避免与在其名称中这样的字符文件的问题,是具有开放模式作为第二个参数:
open THIS, "-|", "cat $ARGV[0]";
这将做同样的事情。 模式“ - |” 代表“从管”和“| - ”代表“管”。 模式的其余部分可为他们(使用>, >>, <, +>, +<
)。 虽然有比这更开放,应该可以满足大多数的事情。
但是你应该避免调用外部程序尽可能。 你可以直接打开这个文件,通过做open THIS, "$ARGV[0]";
,例如,并且有更好的性能。
所以,你可以切什么样的外部程序了呢? 好了,几乎所有的。 但是让我们留在基础:猫,grep的,切,头,尾,uniq的,WC,排序。
猫
好了,没有太多可说的这一个。 请记住,如果可能的话,读取文件一次,并保持它在内存中。 如果该文件是巨大的,你不会做,当然,但几乎总有办法避免读取文件不止一次。
总之,对于猫的基本语法是:
my $filename = "whatever";
open FILE, "$filename" or die "Could not open $filename!\n";
while(<FILE>) {
print $_;
}
close FILE;
这将打开一个文件,并将所有它的内容(“ while(<FILE>)
‘中循环,直到EOF,分配各行’ $_
”),并再次将其关闭。
如果我想直接输出到另一个文件,我可以这样做:
my $filename = "whatever";
my $anotherfile = "another";
open (FILE, "$filename") || die "Could not open $filename!\n";
open OUT, ">", "$anotherfile" or die "Could not open $anotherfile for writing!\n";
while(<FILE>) {
print OUT $_;
}
close FILE;
这将打印行13的“文件OUT
”。 您可以使用STDIN
, STDOUT
和STDERR
在适当的地方为好,而不必首先打开它们。 事实上,“ print
”默认为STDOUT
,而“ die
”默认为“ STDERR
”。
还要注意“ or die ...
”和“ || die ...
”。 运营商or
与||
也就是说,如果第一返回false(这意味着空字符串,空引用,0,等),将仅执行以下命令。 模具命令并显示错误消息停止脚本。
之间的主要区别“ or
”和“ ||
”是当务之急。 如果“ or
”被替换为“ ||
在上面的例子”,因为预期它不会工作,因为线将被解释为:
open FILE, ("$filename" || die "Could not open $filename!\n");
这是不是在所有的期望是什么。 作为“ or
”具有较低的优先级,它的工作原理。 在“线||
”时,向参数open
在括号之间传递,使得有可能使用“ ||
”。
唉,有一些东西,是一个很值得做什么猫:
while(<>) {
print $_;
}
这将打印在命令行中的所有文件,或通过任何标准输入传递。
GREP
因此,如何将我们的“grep”的剧本工作? 我假设的“grep -E”,因为这是在Perl不是简单的grep容易。 无论如何:
my $pattern = $ARGV[0];
shift @ARGV;
while(<>) {
print $_ if /$pattern/o;
}
该“O”传递给$ patttern指示Perl来编译模式只有一次,从而赢得你的速度。 没有风格“的东西,如果COND”。 这意味着,如果条件为真,将只执行“东西”。 最后,“ /$pattern/
”,单独,是相同的“ $_ =~ m/$pattern/
”,这意味着比较$ _用所示的正则表达式。 如果你想标准grep
的行为,即刚子串匹配,你可以写:
print $_ if $_ =~ "$pattern";
切
通常,你最好使用正则表达式组得到比切确切的字符串。 你会做什么用“的sed”,例如。 总之,这里是重现切的方法有两种:
while(<>) {
my @array = split ",";
print $array[3], "\n";
}
这将让你的每一行的第四列,使用“”作为分隔符。 注意@array
和$array[3]
在@
印记手段“阵列”应被视为一个,以及,阵列。 它将接收在当前处理的行中的每个列组成的阵列。 接着,将$
印记装置array[3]
是标量值。 它会返回你所要求的列 。
这不是一个很好的实现,不过,因为“分裂”将扫描整个字符串。 我曾经降低的过程为30分钟到2秒只是通过不使用拆分 - 该线相当大,虽然在哪里。 不管怎样,下面有一个优越的性能,如果线路预计要大,你想要的列是低:
while(<>) {
my ($column) = /^(?:[^,]*,){3}([^,]*),/;
print $column, "\n";
}
它利用正则表达式来获得所需的信息,并仅此而已。
如果你想要的位置列,您可以使用:
while(<>) {
print substr($_, 5, 10), "\n";
}
这将打印从第六起始10个字符(再次,0表示第一个字符)。
头
这是一个非常简单的:
my $printlines = abs(shift);
my $lines = 0;
my $current;
while(<>) {
if($ARGV ne $current) {
$lines = 0;
$current = $ARGV;
}
print "$_" if $lines < $printlines;
$lines++;
}
事情到这里需要注意。 我用“NE”比较字符串。 现在,$ ARGV总是指向当前文件,被读取,所以我跟踪他们曾经我读一个新的文件重新启动我的票。 另请注意,“如果”更传统的语法,对伴随着后固定之一。
我也使用一个简化的语法来获得要被打印的行数。 当您使用“移动”它本身将承担“转移@ARGV”。 另外,还要注意转变,除了修改@ARGV,将返回被转移出来的元素。
作为一个外壳,有一个数字和字符串之间没有区别 - 你只需要使用它。 即使像“2” +事“2”会工作。 事实上,Perl是更为宽松,愉快地处理任何非数字为0,所以你可能要小心那里。
这个脚本是非常低效的,不过,因为它读取所有的文件,不仅所需的线路。 让我们来完善它,并看到一对夫妇在这个过程中重要的关键字:
my $printlines = abs(shift);
my @files;
if(scalar(@ARGV) == 0) {
@files = ("-");
} else {
@files = @ARGV;
}
for my $file (@files) {
next unless -f $file && -r $file;
open FILE, "<", $file or next;
my $lines = 0;
while(<FILE>) {
last if $lines == $printlines;
print "$_";
$lines++;
}
close FILE;
}
“下一个”和“最后”的关键字是非常有用的。 首先,“下一个”会告诉Perl来回到循环条件,如果适用获得下一个元素。 在这里,我们用它来跳过的文件,除非是真正的文件(不是目录)和可读性。 它也将跳过如果我们不能打开文件即使这样。
然后,“最后”是用来立即跳出一个循环。 我们用它来停止读取文件,一旦我们达到了需要的行数。 这是真的,我们读到一条线太多,但在那个位置有“最后一个”清楚地表明,它后线不会被执行。
还有“重做”,这将返回到循环的开始,但没有重新评估的条件,也没有获得下一个元素。
尾巴
我在这里做一个小窍门。
my $skiplines = abs(shift);
my @lines;
my $current = "";
while(<>) {
if($ARGV ne $current) {
print @lines;
undef @lines;
$current = $ARGV;
}
push @lines, $_;
shift @lines if $#lines == $skiplines;
}
print @lines;
好吧,我结合“推”,其中追加值的数组,以“转变”,这需要一些从一个数组的开始。 如果你想有一个堆栈,你可以使用推/ POP或移位/不印字。 它们混合,和你有一个队列。 我把我的队列与最多10个元素$#lines
,这将给我的数组中最后一个元素的索引。 你也可以得到元素的数量@lines
与scalar(@lines)
UNIQ
现在,uniq的不仅消除重复的连续行,这应该是很容易与您到目前为止所看到的东西。 所以,我会消除所有的人:
my $current = "";
my %lines;
while(<>) {
if($ARGV ne $current) {
undef %lines;
$current = $ARGV;
}
print $_ unless defined($lines{$_});
$lines{$_} = "";
}
现在,这里我保持在内存中的整个文件,里面%lines
。 使用的%
印记表明这是一个哈希表。 我使用的线作为键和存储毫无作为价值 - 因为我在值不感兴趣。 我检查其中键与“定义($线{$ _})”时,如果与该键相关联的值被定义或不是将测试存在; 关键字“除非”的作品就像“如果”,但相反的效果,所以它只能打印如果线路没有定义一条线。
也请注意,语法$lines{$_} = ""
作为一种存储在哈希表中的东西。 注意使用的{}
为哈希表,而不是[]
数组。
厕所
这实际上用了很多的东西,我们已经看到:
my $current;
my %lines;
my %words;
my %chars;
while(<>) {
$lines{"$ARGV"}++;
$chars{"$ARGV"} += length($_);
$words{"$ARGV"} += scalar(grep {$_ ne ""} split /\s/);
}
for my $file (keys %lines) {
print "$lines{$file} $words{$file} $chars{$file} $file\n";
}
三个新的东西。 两个是“+ =”运算符,它应该是显而易见的,而“为”表达。 基本上,“为”将指定该阵列所指示的变量的每个元素。 “我”有没有声明变量,但它如果先前声明的是不必要的。 我可以有那些括号内的@array变量。 所述“键%线”表达将返回作为存在哈希表“%线”的阵列,他们键(文件名)。 其余的应该是显而易见的。
第三件事情,我实际上只加修改答案,就是“grep的”。 这里的格式为:
grep { code } array
它将运行“代码”为阵列中的每个元素,传递元素为“$ _”。 然后grep的将返回该代码的计算结果为“真”(不为0,而不是“”,等等)的所有元素。 这避免了计数从连续的空格导致空字符串。
类似的“grep”有“图”,我不会在这里展示。 代替滤波,它将返回通过的“代码”的每个元素的结果形成的阵列。
分类
最后,排序。 这个人是一件容易的事:
my @lines;
my $current = "";
while(<>) {
if($ARGV ne $current) {
print sort @lines;
undef @lines;
$current = $ARGV;
}
push @lines, $_;
}
print sort @lines;
在这里,“排序”将数组排序。 需要注意的是那种可以接收功能来定义的排序标准。 举例来说,如果我想数字排序我可以这样做:
my @lines;
my $current = "";
while(<>) {
if($ARGV ne $current) {
print sort @lines;
undef @lines;
$current = $ARGV;
}
push @lines, $_;
}
print sort {$a <=> $b} @lines;
这里的“ $a
”和“ $b
”接收要比较的元素。 “ <=>
-1,0或1取决于数量是否比其它小于,等于或大于”返回。 对于字符串,“CMP”做同样的事情。
处理文件,目录和其他的东西
至于其他的,基本的数学表达式应该很容易理解。 您可以测试有关文件,这种方式一定的条件:
for my $file (@ARGV) {
print "$file is a file\n" if -f "$file";
print "$file is a directory\n" if -d "$file";
print "I can read $file\n" if -r "$file";
print "I can write to $file\n" if -w "$file";
}
我并不想成为exaustive这里,还有许多其他这样的测试。 我也能做到“来代替”模式,如贝壳的“*”和,像这样的“?”:
for my $file (glob("*")) {
print $file;
print "*" if -x "$file" && ! -d "$file";
print "/" if -d "$file";
print "\t";
}
如果你结合,与“CHDIR”,可以模拟“发现”,以及:
sub list_dir($$) {
my ($dir, $prefix) = @_;
my $newprefix = $prefix;
if ($prefix eq "") {
$newprefix = $dir;
} else {
$newprefix .= "/$dir";
}
chdir $dir;
for my $file (glob("*")) {
print "$prefix/" if $prefix ne "";
print "$dir/$file\n";
list_dir($file, $newprefix) if -d "$file";
}
chdir "..";
}
list_dir(".", "");
在这里我们看到,最后一个功能。 函数声明的语法:
sub name (params) { code }
严格speakings, “(PARAMS)” 是可选的。 我使用的申报参数,“ ($$)
”,意味着我接收两个标量参数。 我可以有“ @
”或“ %
在那里”的可能。 数组“ @_
”已经通过了所有参数。 该生产线“ my ($dir, $prefix) = @_
”是分配该数组变量的前两个元素的只是一个简单的方法$dir
和$prefix
。
这个函数不返回任何东西(这是一个过程,真的),但你可以有刚刚通过添加返回值的函数“ return something;
”给它,并让它返回“东西”。
它的其余部分应该是很明显的。
混音一切
现在,我将提出一个更复杂的例子。 我将展示一些不好的代码解释什么地方错了,然后显示出更好的代码。
对于这第一个例子,我有两个文件,文件names.txt,其姓名和电话号码,该systems.txt,用系统和负责他们的名字。 他们来了:
names.txt中
John Doe, (555) 1234-4321
Jane Doe, (555) 5555-5555
The Boss, (666) 5555-5555
systems.txt
Sales, Jane Doe
Inventory, John Doe
Payment, That Guy
我想的话,要打印第一个文件,以追加到人的名称的系统,如果这个人是负责该系统。 第一个版本可能是这样的:
#!/usr/bin/perl
use strict;
use warnings;
open FILE, "names.txt";
while(<FILE>) {
my ($name) = /^([^,]*),/;
my $system = get_system($name);
print $_ . ", $system\n";
}
close FILE;
sub get_system($) {
my ($name) = @_;
my $system = "";
open FILE, "systems.txt";
while(<FILE>) {
next unless /$name/o;
($system) = /([^,]*)/;
}
close FILE;
return $system;
}
此代码将无法正常工作,虽然。 Perl会抱怨函数太早要检查的原型使用的,但是这只是一个警告。 它会给第8行的误差(第一while循环),抱怨在关闭的文件句柄的readline一个。 这里发生的是“ FILE
”是全球性的,所以功能get_system
正在改变它。 让我们把它改写,固定两件事:
#!/usr/bin/perl
use strict;
use warnings;
sub get_system($) {
my ($name) = @_;
my $system = "";
open my $filehandle, "systems.txt";
while(<$filehandle>) {
next unless /$name/o;
($system) = /([^,]*)/;
}
close $filehandle;
return $system;
}
open FILE, "names.txt";
while(<FILE>) {
my ($name) = /^([^,]*),/;
my $system = get_system($name);
print $_ . ", $system\n";
}
close FILE;
这不会给任何错误或警告,也不会工作。 它仅返回sysems,而不是姓名和电话号码! 发生了什么? 那么,什么情况是,我们正在为一个参考“ $_
呼叫后” get_system
,但是,通过阅读文件, get_system
被覆盖的价值$_
!
为了避免这种情况,我们将做$_
本地内部get_system
。 这将给它一个局部范围,且原始值会回复一次从返回get_system
:
#!/usr/bin/perl
use strict;
use warnings;
sub get_system($) {
my ($name) = @_;
my $system = "";
local $_;
open my $filehandle, "systems.txt";
while(<$filehandle>) {
next unless /$name/o;
($system) = /([^,]*)/;
}
close $filehandle;
return $system;
}
open FILE, "names.txt";
while(<FILE>) {
my ($name) = /^([^,]*),/;
my $system = get_system($name);
print $_ . ", $system\n";
}
close FILE;
而这仍然无法正常工作! 它打印的名称和系统之间的换行符。 嗯,Perl中读取包括它可能具有的所有新行线。 有一个整洁的命令,它从字符串,“删除换行符chomp
”,我们将使用它来解决这个问题。 而且,由于不是每个名字都有一个系统,我们可能,为好,避免打印逗号当这种情况发生:
#!/usr/bin/perl
use strict;
use warnings;
sub get_system($) {
my ($name) = @_;
my $system = "";
local $_;
open my $filehandle, "systems.txt";
while(<$filehandle>) {
next unless /$name/o;
($system) = /([^,]*)/;
}
close $filehandle;
return $system;
}
open FILE, "names.txt";
while(<FILE>) {
my ($name) = /^([^,]*),/;
my $system = get_system($name);
chomp;
print $_;
print ", $system" if $system ne "";
print "\n";
}
close FILE;
这样的作品,但它也恰好是效率极其低下。 我们看整个系统文件,在文件名每一行。 为了避免这种情况,我们会读取系统中的所有数据一次,然后用它来处理的名字。
现在,有时一个文件是这么大,你无法读取到内存中。 当发生这种情况,你应该尝试读入内存来处理其需要的任何其他文件,这样就可以在一个单一的通行证为每个文件所做的一切。 总之,这里是它的第一个优化版本:
#!/usr/bin/perl
use strict;
use warnings;
our %systems;
open SYSTEMS, "systems.txt";
while(<SYSTEMS>) {
my ($system, $name) = /([^,]*),(.*)/;
$systems{$name} = $system;
}
close SYSTEMS;
open NAMES, "names.txt";
while(<NAMES>) {
my ($name) = /^([^,]*),/;
chomp;
print $_;
print ", $systems{$name}" if defined $systems{$name};
print "\n";
}
close NAMES;
不幸的是,这是行不通的。 没有永远的系统出现! 发生了什么事? 好吧,让我们来看看成什么样“ %systems
”包含,通过使用Data::Dumper
:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
our %systems;
open SYSTEMS, "systems.txt";
while(<SYSTEMS>) {
my ($system, $name) = /([^,]*),(.*)/;
$systems{$name} = $system;
}
close SYSTEMS;
print Dumper(%systems);
open NAMES, "names.txt";
while(<NAMES>) {
my ($name) = /^([^,]*),/;
chomp;
print $_;
print ", $systems{$name}" if defined $systems{$name};
print "\n";
}
close NAMES;
输出将是这样的:
$VAR1 = ' Jane Doe';
$VAR2 = 'Sales';
$VAR3 = ' That Guy';
$VAR4 = 'Payment';
$VAR5 = ' John Doe';
$VAR6 = 'Inventory';
John Doe, (555) 1234-4321
Jane Doe, (555) 5555-5555
The Boss, (666) 5555-5555
这些$VAR1/$VAR2/etc
是如何Dumper
显示一个哈希表。 奇数是关键,而随后的偶数都值。 现在我们可以看到,在每个名字%systems
具有前述的空间! 傻正则表达式的错误,让我们来解决它:
#!/usr/bin/perl
use strict;
use warnings;
our %systems;
open SYSTEMS, "systems.txt";
while(<SYSTEMS>) {
my ($system, $name) = /^\s*([^,]*?)\s*,\s*(.*?)\s*$/;
$systems{$name} = $system;
}
close SYSTEMS;
open NAMES, "names.txt";
while(<NAMES>) {
my ($name) = /^\s*([^,]*?)\s*,/;
chomp;
print $_;
print ", $systems{$name}" if defined $systems{$name};
print "\n";
}
close NAMES;
所以,在这里,我们正在积极地除去开头或名称和系统结束的任何空间。 还有其他的方法来形成正则表达式,但是这不是重点。 还有一个问题,这个脚本,你会看到,如果你的“names.txt中”和/或“systems.txt”文件有一个最后的空行。 该警告是这样的:
Use of uninitialized value in hash element at ./exemplo3e.pl line 10, <SYSTEMS> line 4.
Use of uninitialized value in hash element at ./exemplo3e.pl line 10, <SYSTEMS> line 4.
John Doe, (555) 1234-4321, Inventory
Jane Doe, (555) 5555-5555, Sales
The Boss, (666) 5555-5555
Use of uninitialized value in hash element at ./exemplo3e.pl line 19, <NAMES> line 4.
这里发生的是什么走进了“ $name
”变量当处理的空行。 周围有很多办法,但我选择了以下内容:
#!/usr/bin/perl
use strict;
use warnings;
our %systems;
open SYSTEMS, "systems.txt" or die "Could not open systems.txt!";
while(<SYSTEMS>) {
my ($system, $name) = /^\s*([^,]+?)\s*,\s*(.+?)\s*$/;
$systems{$name} = $system if defined $name;
}
close SYSTEMS;
open NAMES, "names.txt" or die "Could not open names.txt!";
while(<NAMES>) {
my ($name) = /^\s*([^,]+?)\s*,/;
chomp;
print $_;
print ", $systems{$name}" if defined($name) && defined($systems{$name});
print "\n";
}
close NAMES;
现在的正则表达式需要名称和系统至少一个字符,而我们测试,看看“ $name
”我们用它之前被定义。
结论
好吧,那么,这些翻译shell脚本的基本工具。 你可以做更多的事情用Perl,但是这不是你的问题,并不会反正这里适合。
正如一些重要议题的基本概述,
可能受到黑客的攻击一个Perl脚本,需要用-T选项,可以运行,这样Perl会抱怨尚未得到妥善处理的任何脆弱的输入。
有图书馆,称为模块,数据库访问,XML和CIA处理,远程登录,HTTP及其他协议。 事实上,存在可以被发现在模块miriads CPAN 。
正如别人所说,如果你使用AWK或SED的,可以翻译成那些与Perl的A2P和S2P 。
Perl可以写在面向对象的方式。
还有的Perl的多个版本。 在撰写本文时,稳定的一个是5.8.8,并有可用的5.10.0。 还有一个在发展一个Perl 6的,但经验告诉大家不要等待它太急切。
有一个免费的,良好的,动手,硬快有关Perl本书叫学习Perl的困难的方法 。 它的风格类似,这非常答案。 这可能是一个好地方,从这里走。
我希望这有助于。
免责声明
我并不想教的Perl,您将需要至少有一定的参考材料。 有良好的Perl的习惯,比如使用指南“ use strict;
”和“ use warnings;
”在剧本的开头,以使其不太宽松的糟糕的代码,或者使用STDOUT和STDERR上的打印线,指示正确的输出管道。
这东西我同意,但我决定还是从显示了常见的shell脚本实用程序模式的基本目标减损。
我不知道什么在你的shell脚本,但不要忘记还有工具,如
也许还有更多。 值得看看周围。
您可能会发现,由于Perl的功率/功能,这是没有这么大的工作中,你可能已经跳通过与各种庆典的功能和实用程序箍做一些事情,出来的Perl本身。
像任何迁移项目,这是非常有用的一些罐头回归测试与这两个解决方案上运行,所以如果你没有这些,我会产生那些第一。
我很惊讶,没有人还没有提到壳牌模块附带Perl核心,它可以让你执行使用函数调用语法外部命令。 例如(改编自概要):
use Shell qw(cat ps cp);
$passwd = cat '</etc/passwd';
@pslines = ps '-ww';
cp "/etc/passwd", "/tmp/passwd";
只要你使用括号,你甚至可以调用其他程序$PATH
,你没有上提use
线,如:
gcc('-o', 'foo', 'foo.c');
需要注意的是Shell
收集了子进程的标准输出,并将它作为一个字符串或数组。 这简化了脚本,但不要去,如果你依赖于一个命令的输出是缓冲可能会引起麻烦的最有效方式。
该模块文档提到一些缺点,诸如壳体内部命令(例如cd
)不能使用相同的语法来调用。 事实上,他们建议该模块不能用于生产系统! 但它却肯定是一个有用的拐杖来依靠,直到你整个移植到“适当”的Perl代码。
内联壳啄被称为system
。 如果你有你想暴露给Perl用户定义的函数,你的运气了。 但是,您可以运行使用相同的环境中运行Perl程序外壳的短位。 您也可以逐步取代用Perl shell脚本的一部分。 开始编写复制shell脚本的功能模块和佩尔利位插入shell脚本,直到你最终有大多的Perl。
有没有壳到Perl的翻译。 有关于csh的对Perl的翻译,你可以通过电子邮件发送脚本长时间运行的笑话,但真的只是汤姆Christainsen翻译为你向你展示冷静的Perl是如何早在90年代初。 兰德尔·施瓦茨上传了SH-到Perl的翻译,但你必须检查上传日期:这是愚人节。 他的剧本只是包裹在一切system
。
不管你做什么,不要失去原来的shell脚本。 :)
我认为,学习Perl和试图写的Perl,而不是外壳是为了更大的利益。 我用记事本的“替换”功能++的帮助下做了一次转移。
不过,我也有类似的问题的一个最初要求,而我试图创建一个围绕一个shell脚本一个Perl包装(即可以执行它)。
我想出了下面的代码,在我的情况下工作。
它可能的帮助。
#!perl
use strict;
use Data::Dumper;
use Cwd;
#Variables read from shell
our %VAR;
open SH, "<$ARGV[0]" or die "Error while trying to read $ARGV[0] ($!)\n";
my @SH=<SH>;
close SH;
sh2perl(@SH);
#Subroutine to execute shell from Perl (read from array)
sub sh2perl {
#Variables
my %case; #To store data from conditional block of "case"
my %if; #To store data from conditional block of "if"
foreach my $line (@_) {
#Remove blanks at the beginning and EOL character
$line=~s/^\s*//;
chomp $line;
#Comments and blank lines
if ($line=~/^(#.*|\s*)$/) {
#Do nothing
}
#Conditional block - Case
elsif ($line=~/case.*in/..$line=~/esac/) {
if ($line=~/case\s*(.*?)\s*\in/) {
$case{'var'}=transform($1);
} elsif ($line=~/esac/) {
delete $case{'curr_pattern'};
#Run conditional block
my $case;
map { $case=$_ if $case{'var'}=~/$_/ } @{$case{'list_patterns'}};
$case ? sh2perl(@{$case{'patterns'}->{$case}}) : sh2perl(@{$case{'patterns'}->{"*"}});
} elsif ($line=~/^\s*(.*?)\s*\)/) {
$case{'curr_pattern'}=$1;
push(@{$case{'list_patterns'}}, $case{'curr_pattern'}) unless ($line=~m%\*\)%)
} else {
push(@{$case{'patterns'}->{ $case{'curr_pattern'} }}, $line);
}
}
#Conditional block - if
elsif ($line=~/^if/..$line=~/^fi/) {
if ($line=~/if\s*\[\s*(.*\S)\s*\];/) {
$if{'condition'}=transform($1);
$if{'curr_cond'}="TRUE";
} elsif ($line=~/fi/) {
delete $if{'curr_cond'};
#Run conditional block
$if{'condition'} ? sh2perl(@{$if{'TRUE'}}) : sh2perl(@{$if{'FALSE'}});
} elsif ($line=~/^else/) {
$if{'curr_cond'}="FALSE";
} else {
push(@{$if{ $if{'curr_cond'} }}, $line);
}
}
#echo
elsif($line=~/^echo\s+"?(.*?[^"])"?\s*$/) {
my $str=$1;
#echo with redirection
if ($str=~m%[>\|]%) {
eval { system(transform($line)) };
if ($@) { warn "Error while evaluating $line: $@\n"; }
#print new line
} elsif ($line=~/^echo ""$/) {
print "\n";
#default
} else {
print transform($str),"\n";
}
}
#cd
elsif($line=~/^\s*cd\s+(.*)/) {
chdir $1;
}
#export
elsif($line=~/^export\s+((\w+).*)/) {
my ($var,$exported)=($2,$1);
if ($exported=~/^(\w+)\s*=\s*(.*)/) {
while($exported=~/(\w+)\s*=\s*"?(.*?\S)"?\s*(;(?:\s*export\s+)?|$)/g) { $VAR{$1}=transform($2); }
}
# export($var,$VAR{$var});
$ENV{$var}=$VAR{$var};
print "Exported variable $var = $VAR{$var}\n";
}
#Variable assignment
elsif ($line=~/^(\w+)\s*=\s*(.*)$/) {
$1 eq "" or $VAR{$1}=""; #Empty variable
while($line=~/(\w+)\s*=\s*"?(.*?\S)"?\s*(;|$)/g) {
$VAR{$1}=transform($2);
}
}
#Source
elsif ($line=~/^source\s*(.*\.sh)/) {
open SOURCE, "<$1" or die "Error while trying to open $1 ($!)\n";
my @SOURCE=<SOURCE>;
close SOURCE;
sh2perl(@SOURCE);
}
#Default (assuming running command)
else {
eval { map { system(transform($_)) } split(";",$line); };
if ($@) { warn "Error while doing system on \"$line\": $@\n"; }
}
}
}
sub transform {
my $src=$_[0];
#Variables $1 and similar
$src=~s/\$(\d+)/$ARGV[$1-1]/ge;
#Commands stored in variables "$(<cmd>)"
eval {
while ($src=~m%\$\((.*)\)%g) {
my ($cmd,$new_cmd)=($1,$1);
my $curr_dir=getcwd;
$new_cmd=~s/pwd/echo $curr_dir/g;
$src=~s%\$\($cmd\)%`$new_cmd`%e;
chomp $src;
}
};
if ($@) { warn "Wrong assessment for variable $_[0]:\n=> $@\n"; return "ERROR"; }
#Other variables
$src=~s/\$(\w+)/$VAR{$1}/g;
#Backsticks
$src=~s/`(.*)`/`$1`/e;
#Conditions
$src=~s/"(.*?)"\s*==\s*"(.*?)"/"$1" eq "$2" ? 1 : 0/e;
$src=~s/"(.*?)"\s*!=\s*"(.*?)"/"$1" ne "$2" ? 1 : 0/e;
$src=~s/(\S+)\s*==\s*(\S+)/$1 == $2 ? 1 : 0/e;
$src=~s/(\S+)\s*!=\s*(\S+)/$1 != $2 ? 1 : 0/e;
#Return Result
return $src;
}
好…
你可以开始你的“的Perl”脚本:
#!/bin/bash
然后,假设bash的安装在那个位置, perl
会自动调用bash的解释器来运行它。
编辑:或者,也许OS将拦截呼叫,并阻止它获得Perl的。 我发现很难追查这个实际上是如何工作的文档。 注释文档将受到欢迎。