非常简单的脚本非法划分为零

时间:2013-11-01 12:12:36

标签: perl divide-by-zero

一个简单的perl脚本:

#!/usr/bin/perl
$num = `sed "s/\([^ ]*\).*/\1/"`;
print "$num";

$total = `sed "s/[^ ]*\(.*\)/\1/"`;
print "$total";

$div = $num / $total;
print "div = $div \n";

当我运行它时:

$ echo 4 10 | ./test.pl 
4 10
Illegal division by zero at ./test.pl line 11.

3 个答案:

答案 0 :(得分:3)

看着你的代码,我有点困惑。首先是如何提出这样一个奇怪的想法,第二个是如何工作。

假设你想要的是解析通过STDIN发送给Perl的字符串。有一种非常简单且惯用的方法,它不涉及sed内部反引号的奇怪用法:使用菱形运算符<>,它是一个从{{1}读取的文件句柄}或STDIN(文件名参数列表),具体取决于给出的内容。

ARGV

通常情况下,我会use strict; use warnings; # always use these two pragmas my $input = <>; # input is the string "4 10" my ($num, $total) = split ' ', $input; # splitting on whitespace my $div = $num / $total; # ... etc chomp输入以删除尾随换行符,但由于我们STDIN在此处的空白处,因此无论如何都会移除换行符。

至于为什么会出现split错误,这是因为您的illegal division by zero变量未定义,在数字上下文中使用时转换为数字$total,例如除法运算符0。如果您使用过/,则会出现错误

use warnings

毫无疑问,这会告诉你一些错误是什么。

答案 1 :(得分:2)

您的代码无法按预期工作,因为您不了解Perl中字符串的转义规则,并且因为您对STDIN的继承方式感到困惑。

在双引号字符串中,删除反斜杠会忽略\(之类的未知转义符。因此:"\(" eq "("\1通常不是替换运算符,而是八进制转义序列:"\1" eq "\01" && "\1" eq "\x01" - 它是字符0x01“标题起点”。所以你的第一行与

相同
$num = `sed "s/([^ ]*).*/\x01/"`;  # this won't change your input

正如您所看到的,正则表达式实际上是错误的,甚至不符合您的输入。

反引号运算符威胁其内容,就像双引号字符串一样,然后将内容传递给shell。 shell也有逃逸,因此必须经常进行双重逃逸。

shell是通过脚本分叉启动的。子进程继承所有文件描述符,包括STDIN。子进程执行shell,然后执行sed进程,该进程从STDIN读取所有输入,这仍然与原始脚本的STDIN相同。

现在执行第二个命令时,STDIN为空,sed打印 nothing 。此时,$total将包含空字符串。当用作数字时,该字符串被解释为零,从而导致您的错误。

解决方案

解决方案是停止将Perl视为一个美化的shell,因为Perl不像实际的sh那样将命令串在一起。要解析您的输入,请使用Perl的内置正则表达式。要阅读输入,请使用builtins,而不是shell惯用语。

首先,使用以下命令启动每个脚本:

use strict;
use warnings;

例如,这迫使我们正确声明所有变量。执行此操作后,运行脚本会发出以下警告和错误:

Argument "" isn't numeric in division (/) at script.pl line 12.
Argument "4 10\n" isn't numeric in division (/) at script.pl line 12.
Illegal division by zero at script.pl line 12.

所以你实际上在计算"4 10 \n" / "",而不是你想要的。

要阅读输入,我们使用<>钻石运算符。然后每一行都登陆$_默认变量。我们可以使用像/([0-9]+)/这样的正则表达式来提取数字,但我们宁愿将每一行拆分为空格:

while (<>) {
  my ($num, $total) = split;
  print $num / $total, "\n";
}

然后我们执行除法,并将结果与​​换行符一起打印出来。如果您使用任何非古老的perl,您还可以use feature 'say'启用say函数:与print完全相同,但会自动添加换行符。

您可以在命令行中指定它们,而不是通过STDIN等提供参数。命令行参数可通过@ARGV数组访问。现在:

my ($num, $total) = @ARGV;
say $num / $total;

调用:

$ perl script.pl 4 10

对于某些任务,Perl不是适合的工具。如果要编写shell脚本,请编写实际的shell脚本:

#!/bin/bash
input="4 10"
num=`echo $input | sed 's/\([^ ]*\).*/\1/'`
total=`echo $input | sed 's/[^ ]*\(.*\)/\1/'`
echo `bc <<<"scale = 5; $num / $total"`

答案 2 :(得分:1)

更改这样的代码,答案显而易见:

#!/usr/bin/perl

$num = `sed "s/\([^ ]*\).*/\1/"`;

print "num = $num\n";

$total = `sed "s/[^ ]*\(.*\)/\1/"`;

print "total = $total\n";

$div = $num / $total;

print "div = $div\n";