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

あなたのコードを見て、私はやや混乱しています。第一に、そのような奇妙なアイデアをどのように思いつくか、そして第二に、それがどのように機能するかについてです。

あなたが望むのは、 経由で Perl に送信された文字列を解析することだと思いますSTDINsedこれを行うための非常に単純で慣用的な方法があります。これは、バッククォート内の奇妙な使用法を含みません: or (ファイル名引数リスト)<>から読み取るファイルハンドルであるダイヤモンド演算子を使用します。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から入力しSTDINて末尾の改行を削除しますが、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は通常、置換演算子ではなく、8 進数のエスケープ シーケンスです。"\1" eq "\01" && "\1" eq "\x01"– 文字 0x01「見出しの開始」です。したがって、最初の行は次と同じです

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

ご覧のとおり、正規表現は実際にはバグが多く、入力と一致しません。

バックティック演算子は、二重引用符で囲まれた文字列とまったく同じように内容を脅かし、その内容をシェルに渡します。シェルにはエスケープもあるため、二重のエスケープが必要になることがよくあります。

シェルは、スクリプトから fork することによって起動されます。子プロセスは、STDIN を含むすべてのファイル記述子を継承します。子はシェルを実行し、プロセスをフォークして、 STDIN からすべてのsed入力を読み取ります。これは、元のスクリプトの STDIN と同じです。

2 番目のコマンドを実行すると、STDIN は空になり、何もsed出力されません。この時点で、空の文字列が含まれます。数値として使用すると、その文字列はゼロとして解釈され、エラーが発生します。$total

ソリューション

解決策は、Perl を美化されたシェルとして扱うのをやめることです。なぜなら、Perl は実際のコマンドを一緒に並べるのに適していないからshです。入力を解析するには、Perl の組み込み正規表現を使用します。入力を読み取るには、シェルのイディオムではなく組み込みを使用します。

まず、すべてのスクリプトを次のように開始します。

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 は適切なツールではありません。シェル スクリプトを作成する場合は、実際のシェル スクリプトを作成します。

#!/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 に答える