0

一个简单的 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.
4

3 回答 3

3

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

假设您想要的是解析通过STDIN. 有一种非常简单和惯用的方法可以做到这一点,它不涉及sed内部反引号的奇怪用法:使用菱形运算符,它是一个从或(文件名参数列表)<>读取的文件句柄,具体取决于给定的。STDINARGV

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输入 fromSTDIN以删除尾随的换行符,但由于我们split在这里使用空格,因此无论如何都会删除换行符。

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

Use of uninitialized value $total in division (/) at script.pl line 10.

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

于 2013-11-01T13:54:06.957 回答
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打印任何内容。此时,$total将包含空字符串。当用作数字时,该字符串被解释为零,从而导致您的错误。

解决方案

解决方案是停止将 Perl 视为一个美化的 shell,因为 Perl 在将命令串在一起方面不如实际的好sh。要解析您的输入,请使用 Perl 的内置正则表达式。要读取输入,请使用内置函数,而不是 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"`
于 2013-11-01T14:00:35.860 回答
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";
于 2013-11-01T12:18:54.433 回答