수학 방정식을 결과로 대체

수학 방정식을 결과로 대체

미리 정의된 정규식을 사용하여 찾은 방정식을 파일의 결과로 바꾸는 가장 좋은 방법은 무엇입니까?

각 방정식 공식은 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

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.) 일치는 탐욕적이지 않으므로 한 줄에 여러 블록이 작동하지만 이는 닫는 구분 기호가 표현식의 일부로 나타날 수 없음을 의미합니다. 즉, 제가 사용하는 이유는 다음과 같습니다. ]](괄호를 사용하면 충돌이 발생할 수 있습니다. 예를 들어 실패하므로 반대의 경우 ((3*(a+(b+c))))와 마찬가지로 괄호를 공백으로 구분해야 합니다 .)((3*(a+(b+c) ) ))

\[\[괄호는 정규식에서 여전히 특별하므로 정규식에서 일치 및 \]\]; 괄호는 내부 부분을 캡처하는 데 사용됩니다. 하지만 인쇄된 개행 문자를 처리하는 더 좋은 방법이 있을 수 있습니다 bc.

각 산술 블록에 대해 새로운 프로세스를 시작하는 것은 다소 부담스러워 보일 수 있지만 매우 간단한 솔루션입니다.

이는 악의적인 입력에 대해 안전하지 않습니다.블록의 내용이 쉘로 전달되면 bc산술 표현식의 모든 쉘 구문이 처리됩니다. 예를 들어 다음 uname명령이 실행됩니다.

[[ $(uname -a >&2 ) ]]

이 문제는 Perl 파이프를 열어 해결할 수 있지만 bc한 줄의 코드에 맞지 않을 수도 있습니다.

답변2

예제 표현식은 perl직접 평가할 수 있는 것처럼 보입니다. 이 경우 다음을 bc사용하여 각 표현식을 호출하지 않아도 됩니다.

perl -pe 's{\[\[([\d\s./*+-]+)\]\]}{eval$1}ge' < "$infile"

./*+-여기서는 명령 주입 취약점을 방지하기 위해 숫자와 공백이 포함된 표현식만 처리됩니다 .

산술 표현식과도 호환 되므로 zshbash에서 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 설치에는 없었기 때문입니다)bcbc알다awk우리는 이미 그것을 그렇게 부릅니다.

그러나 실행할 때마다 하위 쉘이 생성되므로 속도가 느려집니다 eval.

관련 정보