事前定義された正規表現を使用して見つかった方程式をファイルの結果に置き換える最善の方法は何ですか?
各方程式はbc -l
(浮動小数点処理が可能な基本計算機)と互換性があると仮定されます。
たとえば、方程式が[[
andで区切られているとします]]
(たとえば)。
入力は次のとおりです
Results show that each unit should generate
approx. [[7*9/2.0]]Wh per day.
All the same ...
希望の出力:
Results show that each unit should generate
approx. 31.5Wh per day.
All the same ...
私の最善の試み:
while read line; do
if [[ $line =~ \[\[.*\]\] ]]; then
equ=`echo "$line" | sed "s|.*\[\[||" | sed "s|]].*||"`
res=`echo "$equ" | bc -l | awk '{print $1+0}'`
new_line=`echo $line | sed "s|\[\[.*]]|$res|"`
echo $new_line
else
echo $line
fi
done < $infile
出力:
Results show that each unit should generate
approx. 31.5Wh per day.
All the same ...
しかし、(whileループなしで)より簡単な方法があるかどうか疑問に思います。
また、1行あたりの方程式が1つしかない場合にも機能します。
答え1
bc -l
互換性があると言いましたか?最善の選択は逃げると思いますbc -l
。 Perlを使えば簡単にできます。これがあればmath.txt
:
two times [[3*7]] is [[2*3*7]]
[[scale=6; a=4; a*s(3.141/4)]]
[[...]]
その後、スキャンしてチャンクが渡されますbc -l
。
$ perl -pe 's,\[\[(.*?)\]\], $a=qx/echo "$1" | bc -l /; chomp $a; $a ,ge' math.txt
two times 21 is 42
2.828008
[[
ただ と間の内容をキャプチャして]]
入力にプッシュbc -l
し、括弧で囲まれたブロックを出力に置き換えます。
これはブロックごとに新しいインスタンスを実行するため、bc
変数の割り当てはブロック間で維持されません。 (ただし、上記のようにブロックごとに設定できますscale
。)一致は貪欲ではないので、1行に複数のブロックが機能しますが、これは閉じた区切り文字が式の一部として表示されないことを意味します。つまり、私が使用する理由は次のとおりです。]]
(括弧を使用すると競合が発生する可能性があります。たとえば、失敗するため、逆の場合((3*(a+(b+c))))
と同様に、括弧をスペースで区切る必要があります。)((3*(a+(b+c) ) ))
\[\[
括弧は正規表現では依然として特別なので、正規表現で一致します\]\]
。括弧は内部部分を捕捉するために使用されます。ただし、印刷された改行文字を処理するより良い方法があるかもしれませんbc
。
各算術ブロックに対して新しいプロセスを開始することは、やや面倒に見えるかもしれませんが、非常に簡単な解決策です。
これは悪意のある入力に対して安全ではありません。ブロックの内容がシェルに渡されると、bc
算術式のすべてのシェル構文が処理されます。たとえば、次のuname
コマンドが実行されます。
[[ $(uname -a >&2 ) ]]
この問題はPerlパイプを開くことで解決できますが、bc
1行のコードに収まらない可能性があります。
答え2
サンプル式はperl
直接評価できるようです。この場合、以下をbc
使用して各式を呼び出す必要はありません。
perl -pe 's{\[\[([\d\s./*+-]+)\]\]}{eval$1}ge' < "$infile"
./*+-
ここでは、コマンドインジェクションの脆弱性を防ぐために、数字とスペースを含む式のみが処理されます。
算術式とも互換性があるため、zsh
bashからzshに切り替えることがオプションの場合は、次のようにすることができます。
set -o extendedglob
print -r -- ${"$(<$infile)"//(#b)\[\[([[:digit:][:space:].\/*+-]##)\]\]/$((match[1]))}
bc -l
次のように計算することもできます。
set -o extendedglob
print -r -- ${"$(<$infile)"//(#b)\[\[([[:digit:][:space:].\/*+-]##)\]\]/$(bc -l <<<$match[1])}
答え3
awkを使用してください。
$ cat tst.awk
{
while ( match($0,/\[\[.*]]/) ) {
$0 = substr($0,1,RSTART-1) eval(substr($0,RSTART+2,RLENGTH-4)) substr($0,RSTART+RLENGTH)
}
print
}
function eval(equation, cmd,line,rslt) {
rslt = "FAILED:" equation
if ( equation !~ /[\047"]|system/ ) {
cmd = "awk \047BEGIN{print " equation "; exit}\047"
if ( (cmd | getline line) > 0 ) {
rslt = line+0
}
close(cmd)
}
return rslt
}
$ awk -f tst.awk file
Results show that each unit should generate
approx. 31.5Wh per day.
All the same ...
数学の部分を実行するawk
のではなく、再び電話をかけました。なぜなら私たちが存在したいのですが(私のcygwinのインストールにはなかったからです)bc
bc
知るawk
私たちはすでにそれをそう呼んでいます。
ただし、実行するたびにサブシェルが作成されるため、速度が遅くなりますeval
。