您的代码没有按预期工作,因为您不了解 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"`