あなたのコードは、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"`