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
(今回はその機能をどのように定義しましたか?)。
これらすべては時間が経つと間違いなく変わりますが、少なくとも始まりはあります。