web-dev-qa-db-ja.com

bcを呼び出さずにコマンドラインに入力された数値を数学演算する

時々私はいくつかの数学演算を実行する必要があります。 bcまたはecho $(( 6/2 ))を使用できることはわかっています。 bcが入力を読み取るための独自の関数を作成しました。ただし、次のように入力するのに時間がかかることがあります:_bc "6/2"。だから私はこの質問があります:

コマンドラインで数値の数学演算を実行する方法をzsh/bashに教える方法はありますか? 1つの例は、数千を超える単語です。

$ 6/2
$ 3.0

これは、zsh/bashが番号を認識し、つまりbcを呼び出す必要があることを意味します。

5
waldauf

ショートカット Alt-c (バッシュ)

Bashでは、readlineユーティリティを使用して、Word calcを先頭に配置し、これまでに書き込まれたテキストを二重引用符で囲むキーシーケンスを定義できます。

_ bind '"\ec": "\C-acalc \"\e[F\""'
_

それを実行したら、たとえば_23 + 46 * 89_と入力します。 Alt-c 取得するため:

_ calc "23 + 46 * 89"
_

Enterキーを押すだけで、計算はcalcとして定義された関数によって実行されます。これは、単純な場合もあれば、はるかに複雑な場合もあります。

_ calc () { <<<"$*" bc -l; }
_

(+)エイリアス

エイリアスを定義できます:

_alias +='calc #'
_

これまでに入力したコマンドライン全体をコメント化します。次のように入力します。

_ + (56 * 23 + 26) / 17
_

Enterキーを押すと、行がcalc #(56 * 23 + 26) / 17に変換され、コマンドcalcが呼び出されます。 calcがこの関数の場合:

bash

_ calc(){ s=$(HISTTIMEFORMAT='' history 1);   # recover last command line.
         s=${s#*[ ]};                        # remove initial spaces.
         s=${s#*[0-9]};                      # remove history line number.
         s=${s#*[ ]+};                       # remove more spaces.
         eval 'bc -l <<<"'"$s"'"';           # calculate the line.
       }
_

ksh

_ calc(){ s=$(history -1 |                          # last command(s)
             sed '$!d;s/^[ \t]*[0-9]*[ \t]*+ //'); # clean it up 
                                                   # (assume one line commads)
         eval 'bc -l <<<"'"$s"'"';                 # Do the math.
       }
_

zsh zshでは、_+_エイリアスも_#_文字も使用できません。

値は次のように出力されます。

_ $ + (56 * 23 + 26) / 17
 77.29411764705882352941
_

_+_のみが必要で、文字列は引用符で囲まれ(グロブなし)、シェル変数が受け入れられます。

_ $ a=23
 $ + (56 * 23 + $a) / 17
 77.11764705882352941176
_

(+)関数

いくつかの制限がありますが、これは(bashの)関数を使用してリクエストに最も近いものです。

_+() { bc -l <<< "$*"; }
_

これは次のように機能します:

_$ + 25+68+8/24
93.33333333333333333333
_

問題は、シェルの解析が回避されず、_*_(たとえば)がpwd内のファイルのリストに展開される可能性があることです。

(空白の)スペースなしでコマンドラインを書くなら、おそらく大丈夫でしょう。

$(...)のようなものは展開されるので注意してください。

安全な解決策は、評価される文字列を引用することです:

_$ + '45 + (58+3 * l(23))/7'
54.62949752111249272462

$ + '4 * a(1) * 2'
6.28318530717958647688
_

これは、あなたの__bc "6/2"_よりも2つ文字だけ短いですが、_+_は私にとってより直感的に見えます。

18
Isaac

私はbashのバリアントを使用しています マジックエイリアスハック

asis() { bc <<< "$(history 1 | Perl -pe 's/^ *[0-9]+ +[^ ]+ //')"; }
alias c='asis #'

次に:

$ c 1+1
2
$ c -10 + 20 / 5
-6
$ c (-10 + 20) / 5
2
$ c 2^8 / 13
19
$ c scale=5; 2^8 / 13
19.69230

魔法は、エイリアス展開が通常のコマンドライン処理の前にbefore行われるという事実です。これにより、残りの引数がコメント文字の後に続くコマンドを作成できます。実装機能は、historyコマンドを使用して検索します。

この魔法により、*(、および文字通り他の文字。しかし、これは、$もリテラルです:

$ x=5.0
$ y=-1.2
$ z=4.7
$ c ($x + $y) > $z
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: $
(standard_in) 1: illegal character: $

私はこれを少しのブートストラップで回避します:

$ echo "x=$x; y=$y; z=$z"
x=5.0; y=-1.2; z=4.7
$ c x=5.0; y=-1.2; z=4.7; (x + y) > z
0

次のように入力した方がいいかもしれません:bc Enter 1 + 1 EnterControl+D


補足として、bcのデフォルト設定(scaleなど)を$HOME/.bcと私はbc -lエイリアスで。これらの変更は必要ありません。

10
bishop

zshでは、次のようなことができます。

autoload zcalc
accept-line() {
  if [[ $BUFFER =~ '^[ (]*[+-]? *(0[xX]|.)?[[:digit:]]+[^[:alnum:]]' ]]; then
    echo
    zcalc -e $BUFFER
    print -rs -- $BUFFER
    BUFFER=
  fi
  zle .$WIDGET
}
zle -N accept-line

accept-lineウィジェットを再定義します(マッピングされた Enter)現在の行がオプションで任意の数の(sをプレフィックスとして付けられた数値(10進数または16進数)で始まるかどうかをチェックするユーザー定義のウィジェットに、その後に非数字文字を探して、 7Zipまたは411toppm

それが一致する場合は、zcalcに渡します(シェル変数とすべてのzsh数学関数および数値スタイルを使用できますが、任意の精度をサポートしていないため、bcよりも便利です)、履歴に行を追加し、空のコマンドを受け入れます。

次のような数字を含む行を入力すると、混乱が生じる可能性があることに注意してください。

cat << EOF
213 whatever
EOF

または:

var=(
  123 456
)
9

Stéphaneのzshの回答 に触発され、合計で Isaacのbshの回答 より長いが、操作は短い:

trap '[[ $_ =~ [[:digit:]] ]] && bc -l <<< "$_"' ERR

これには、毎回「そのようなファイルまたはディレクトリはありません」というエラーが表示されるという副作用もあります。

$ foozle
-bash: foozle: command not found
$ 1+2+3
-bash: 1+2+3: command not found
6
$ 6/3
-bash: 6/3: No such file or directory
2.00000000000000000000

実行する予定の操作によっては、正規表現を厳しくすることができます。

これは、特定のコマンドが存在しない場合にERRトラップを呼び出すというbash動作を使用します。最後のコマンド($_)に数字が含まれている場合、その「コマンド」に対してbcを実行します。


Stéphaneからのヒント のおかげで、結果を達成するための少しクリーンな方法をここに示します(bash 4.0以降が必要です 機能を導入 ):

if ! declare -F command_not_found_handle > /dev/null
then
  command_not_found_handle() { 
    if [[ "$@" =~ [[:digit:]] ]]; then 
      bc <<< "$@"; 
    else
      printf 'bash: %s: command not found\n' "$1" >&2
      return 127
    fi
  }
else
  echo Unable to set up the handler function, sorry
fi

この関数は、コマンドが見つからないときに呼び出されます。そのコマンドに数字が含まれている場合は、bcを介してそれをスローします。それ以外の場合は、bashの株式メッセージと同様のメッセージを発行し、127の終了コードを返します。

6
Jeff Schaller

次のコマンドラインは入力がかなり簡単です。

<<< 5+4 bc
<<< 6/3 bc
<<< 7*2 bc

かっこで少し複雑です(引用符またはエスケープする必要があります)。

<<< "(5+4)*2/3" bc
<<< \(5+4\)*2/3 bc
4
sudodus

Bashでこれを行うもう1つの不完全な方法は、すべてのコマンドで実行されるDEBUGトラップを使用することです。 extdebugを設定すると、トラップハンドラーがメインコマンドの実行を阻止できるため、「コマンドが見つかりません」エラーが発生しません。

$ cat bash_calc.sh
shopt -s extdebug
debug_calc() {
    local re='^[ (]*-?[0-9]'
    if [[ $BASH_COMMAND =~ $re ]]; then
        echo "$BASH_COMMAND" | bc -l
        return 1
    fi
}
trap debug_calc DEBUG
$ . ./bash_calc.sh
$ 123 * 456
56088
$ 123/456
.26973684210526315789

トラップは、変数またはファイル名のパターンを展開する前に完全なコマンドラインを取得するため、引用符で囲まれていない*機能します。 (しかし、計算でシェル変数を使用しても機能しません。)

ただし、引用符で囲まれていない括弧は構文エラーを引き起こすため、これも完全ではありません。

(上記の正規表現に Stéphane's answer からニックネームを付けました。)

1
ilkkachu

Exprはどうですか?

$ expr 6 / 2
3
1
Hopping Bunny