Делать математическую операцию на номерах, набранных в командной строке без вызова BC

Я предлагаю вам проверить команду expect. Аналогичная ситуация (с ssh) должна выглядеть как ...

#!/usr/bin/expect
[...]
spawn ssh [lindex $argv 1]@root@[lindex $argv 0]@x.y.z.j -tt
expect "*?assword" {
   send "$PASSWORD\r"
[...]
5
07.12.2018, 07:32
7 ответов

Ярлык Alt-c(bash)

С помощью bash, используя утилиту readline, мы можем определить последовательность клавиш, чтобы поместить слово calcв начало и заключить написанный текст в двойные кавычки:

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

Выполнив это, вы набираете 23 + 46 * 89, например, затем Alt-c , чтобы получить:

 calc "23 + 46 * 89"

Просто нажмите Enter, и математические вычисления будут выполнены функцией, определенной как calc, которая может быть как простой, так и намного более сложной:

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

a (+ )Псевдоним

Мы можем определить псевдоним:

alias +='calc #'

Который прокомментирует всю набранную командную строку. Вы набираете:

 + (56 * 23 + 26) / 17

Когда вы нажмете ввод, строка будет преобразована в calc #(56 * 23 + 26) / 17и будет вызвана команда calc. Если calc это функция:

 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.
       }

 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 не допускает ни псевдонима +, ни символа #.

Значение будет напечатано как:

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

Требуется только +, строка заключена в кавычки (нет подстановок ), допустимы переменные оболочки:

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

a (+ )Функция

С некоторыми ограничениями, это самое близкое, что я получил к вашему запросу с функцией (в 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", но +кажется мне более интуитивным.

19
27.01.2020, 20:31

В 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(, сопоставленный с Введите)в пользовательский -виджет, который проверяет, начинается ли текущая строка с числа (десятичного или шестнадцатеричного )опционально с префиксом любого количества (s, ища после этого не -alnum символ, чтобы избежать ложных срабатываний для таких команд, как 7zipили 411toppm.

Если это совпадает, мы передаем его вzcalc(более полезное, чем bc, поскольку оно может использовать переменные оболочки и все математические функции zsh и числовые стили, но не поддерживает произвольную точность ), добавьте строку в историю и принять пустую команду.

Учтите, что это может привести к путанице, если вы введете строку с цифрами в таких вещах, как:

cat << EOF
213 whatever
EOF

Или:

var=(
  123 456
)
9
27.01.2020, 20:31

Вдохновлен ответом Стефана zsh и длиннее, чем ответ Исаака bash в целом, но короче в работе:

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

Регулярное выражение может быть ужесточено в зависимости от ожидаемых операций.

Этот (ab )использует поведение bash для вызова ловушки ERR, когда данная команда не существует. Если последняя команда (в$_)содержит цифру, то выполняется bcдля этой «команды».


Благодаря подсказке Стефана , вот более чистый способ достижения результата (требуется 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
27.01.2020, 20:31

Другим несовершенным способом сделать это в Bash было бы использование ловушки DEBUG, которая срабатывает при каждой команде. С установленным extdebugобработчик trap может предотвратить запуск основной команды, поэтому вы не получите ошибки «команда не найдена».

$ 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

Ловушка получает полную командную строку перед расширением переменных или шаблонов имен файлов, поэтому *без кавычек работает. (Но использование переменных оболочки в вычислениях не работает.)

Однако скобки без кавычек по-прежнему вызывают синтаксическую ошибку, так что это тоже не идеально.

(Я взял регулярное выражение выше из ответа Стефана .)

1
27.01.2020, 20:31

Я использую вариант хака magic alias от 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

Волшебство заключается в том, что расширение псевдонима происходит до обычной обработки командной строки, что позволяет нам создать команду, оставшиеся аргументы которой следуют за символом комментария, который реализующая функция находит с помощью команды 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

Лучше наберите:bcEnter 1 + 1 EnterControl+D


В качестве примечания: у меня есть настройки по умолчанию bc(, такие какscale$HOME/.bc, и я использую bc -lв псевдониме. Для вашего использования эти модификации могут не потребоваться.

10
27.01.2020, 20:31

Следующие командные строки довольно просты для ввода,

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

и несколько сложнее со скобками (должны быть заключены в кавычки или экранированы ),

<<< "(5+4)*2/3" bc
<<< \(5+4\)*2/3 bc
4
27.01.2020, 20:31

А как насчет выражения?

$ expr 6 / 2
3
1
27.01.2020, 20:31

Теги

Похожие вопросы