m4を使用してbcと対話する

m4を使用してbcと対話する

m4でインタラクティブに使用したいと思いますbc。インスタントユースケースはinclude機能を追加することですが、bc後で他の目的に使用することもできます。しかし、問題があります。以下はbc単独で使用される例です。

$ bc
bc 1.07.1
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
3+7
10
sqrt(12345)
111
quit
$

以下はインタラクティブに使用されるのと同じ例ですm4

$ m4 -iP - | bc
3+7
10
sqrt(12345)
111
quit

$

2つの違いがあります。まず、m4そのバージョンではバナーが表示されませんでした。第二に、このm4バージョンはquitすぐには実行されず、後で実行する必要がありますquit

なぜこのようなことが起こるのでしょうか?m4バージョンをバージョンのように機能させるにはどうすればよいですかbc

答え1

私が思いついた結果は次のとおりです。まだ努力していますが、期待通りに動作しているようです。シェルbcスクリプトは次の場所/usr/local/bin/bcにあります。$PATH/usr/bin/bc

#! /bin/sh

HELP="usage: /usr/local/bin/bc [options] [file ...]
  -h              print usage message and exit
  -s              synclines; same as m4 -s
  -v              print bc version information and exit
  -D name[=value] define macro name, optionally give it a value
  -I directory    location of m4 include directory
  -U name         undefine macro name
  NOTE: bc -ql are always active; bc -sw are unavailable;
        m4 -P is always active; m4 -i may be active"

USAGE="-[hsv] [-D name[=value]] [-I directory] [-U name]\n"

# file locations
BC=/usr/bin/bc
BCPRE=/usr/local/bin/bc.prelude
M4=/usr/bin/m4
M4PRE=/usr/local/bin/bc.m4.prelude
SCRIPT="./bc.script.$(date +%Y%m%d)"

# collect command options
M4OPTS=""
while getopts "hsvD:I:U:" flag
do  case $flag in
    h) echo "$HELP"; exit 0;;        # print help message and exit
    s) M4OPTS="$M4OPTS -s";;         # synclines
    v) $BC -v; exit 0;;              # print bc version and exit
    D) M4OPTS="$M4OPTS -D $OPTARG";; # define macro name[=value]
    I) M4OPTS="$M4OPTS -I $OPTARG";; # specify include directory
    U) M4OPTS="$M4OPTS -U $OPTARG";; # undefine macro name
    ?) echo "Usage: $0 $USAGE"; exit 2;;
    esac
done
shift $(($OPTIND - 1))

# initialize random number generator
SRAND=$(mktemp /tmp/bc.srand.XXXXXX)
echo "srand = $(od -vAn -N4 -tu4 < /dev/urandom)" > $SRAND
trap "rm -f $SRAND" 0 1 2 3 15

# execute bc
if   test -t 0
then script -qfa bc.script.$(date +%Y%m%d) -c \
     "rlwrap -aN sh -c \"$M4 -iP $M4PRE $SRAND $BCPRE $@ - | $BC -ql\""
else $M4 -P $M4PRE $SRAND $BCPRE $@ - | $BC -ql
fi

これはbc.prelude

scale = 0; ibase = obase = A

define int(x) { # truncate toward zero
    auto s; s = scale; scale = 0; x /= 1; scale = s; return x }
define frac(x) { return x - int(x) }
define abs(x) { if (x<0) return -x else return x }
define sgn(x) { if (x<0) return -1; if (x>0) return 1; return 0 }
define max(a,b) { if (a<b) return b else return a }
define min(a,b) { if (a<b) return a else return b }
define mod(n,m) {
    auto x, s; s = scale; scale = 0; x = n % m; scale = s
    if (x < 0) return x + m; return x }
define gcd(a,b) {
    auto x, s; s = scale; scale = 0
    while (b != 0) { x = mod(a,b); a = b; b = x }
    scale = s; return a }
define push(*stack[],value) { return(stack[++stack[0]]=value) }
define pop(*stack[]) {
    if (stack[0]<1) stack[-1] else return stack[stack[0]--] }

define srand(seed) { # seed on half-open range [0,2^32)
    auto i, old_scale; old_scale = scale; scale = 0; srand[0] = seed
    for (i=1; i<32; i++) # initialize shuffle box
        srand = srand[i] = (69069 * srand[i-1] + 1234567) % 4294967296
    scale = old_scale; return seed }
junk = srand(srand) # initial seed from /dev/urandom via bc shell script
define rand() { # pseudorandom integer on half-open range [0,2^32)
    auto j, n, old_scale; old_scale = scale; scale = 0
    srand = (69069 * srand + 1234567) % 4294967296
    n = srand[(j = 32 * srand / 4294967296)]
    srand[j] = srand; scale = old_scale; return n }
define randint(lo,hi) { # pseudorandom integer from lo to hi inclusive
    auto randint, old_scale; old_scale = scale; scale = 0
    randint = (hi - lo + 1) * rand() / 4294967296 + lo
    scale = old_scale; return randint }

これはbc.m4.prelude

m4_divert(-1)

m4_define(error, `{ print $1, "\n"; 0/0 }')

m4_define(with_scale,
    { junk = push(saved_scale[],scale); scale = $1;
    $2;
    scale = pop(saved_scale[]) })

m4_define(load, `m4_include($1)')

m4_define(quit, `m4_m4exit')

m4_divert`'m4_dnl

以下は、次の対話の例ですbc

$ bc
rand()
2525043732
with_scale(20,a(1)*4)
3.14159265358979323844
define fib(n) {
    auto f1, f2, f3
    f1 = 0; f2 = 1
    while (n-- > 0) {
        f3 = f1 + f2
        f1 = f2; f2 = f3 }
    return f2 }
fib(37)
39088169
quit
$

コマンド履歴がreadline正しく機能しているようで、自動レコード収集(1日に1つのファイル、同じファイル内の複数のセッション)が非常に役に立つことを願っていますbc(今回はその機能をどのように定義しましたか?)。

これらすべては時間が経つと間違いなく変わりますが、少なくとも始まりはあります。

関連情報