![在 zsh 中舍入/截断字符串中的数字(或使用外部工具)](https://linux22.com/image/29339/%E5%9C%A8%20zsh%20%E4%B8%AD%E8%88%8D%E5%85%A5%2F%E6%88%AA%E6%96%AD%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%AD%E7%9A%84%E6%95%B0%E5%AD%97%EF%BC%88%E6%88%96%E4%BD%BF%E7%94%A8%E5%A4%96%E9%83%A8%E5%B7%A5%E5%85%B7%EF%BC%89.png)
我正在尝试做一个界面,bc
以便可以直观地使用它,而不会“卡在”其中而烦恼。我没有时间对其进行太多测试,因为我陷入了另一个细节,即如何呈现结果(我认为是一个字符串)。
四舍五入或截断并不重要,两者都可以。看看下面,你立刻就明白了。我使用 zsh,但外部工具就可以了,因为我不会在任何时候或其他关键上下文中使用它,它只是一个桌面工具。
calc () {
result=`bc <<EOF
scale=3;
$@
EOF`
echo ${result//%0/} # doesn't work; will only remove one zero
# also, if there are only zeroes, should
# remove dot as well - what about .333, etc.?
}
编辑
下面的解决方案给我留下了深刻的印象,尤其是如何noglob
摆脱引号!
但是,使用点来强制浮点计算是我永远不会记得的事情(你不使用这样的普通计算器)。它甚至有点冒险,特别是对于浮点会产生完全不同的结果(很可能是您想要的结果)并不明显的计算而言。
另外,下面的计算显示了一些不漂亮的输出(太长的实数和尾随点)。
也许我应该将这个(其中一些)与@Gille 的输出格式结合起来回答以下?当我让它完美工作时,我会将结果发布在这里。 (编辑:所接受的回答效果很好。请务必阅读该答案的评论。)
calc () {
echo $(($*));
}
alias calc='noglob calc'
calc 1./3
0.33333333333333331
calc 7.5 - 2.5
5.
答案1
使用zsh
自己的算术,你可以这样做:
calc() printf '%.6g\n' $(($*))
alias 'calc=noglob calc'
但这意味着您需要输入数字,以便123.
将它们视为浮点并触发浮点计算。
.
您可以通过附加任何不属于十六进制数字(或其他基数的数字)或变量名称或类型数字的十进制数字序列来解决此问题,12e-20
例如:
setopt extendedglob
calc() printf '%.6g\n' $((${*//(#bm)(([0-9.]##[eE][-+][0-9]##|[[:alnum:]_#]#[.#_[:alpha:]][[:alnum:]_#]#)|([0-9]##))/$MATCH${match[3]:+.}}))
alias 'calc=noglob calc'
到那时您可能会认为使用bc
和修剪尾随 0 更容易。
也可以看看awk
:
calc() awk "BEGIN{print $*}"
它支持较少的运算符和数学函数,但可能对您来说足够了。
答案2
如果我理解正确的话,您想要删除尾随零和尾随点。在这种情况下,如果EXTENDED_GLOB
设置了,您可以使用
${result//%.#0##/}
即:在字符串 ( ) 的末尾%
匹配零个或多个点 ( .#
),后跟一个或多个零 ( 0##
)。
""
但如果result
是,这将返回0
。您可以对第一个替换进行另一个替换,以将返回值恢复为0
:
${${result//%.#0##/}:-0}
答案3
bc
可以将结果打印为长整数或小数。这是一个脚本,它将分成多行的长整数连接在一起,并删除小数中小数点后的尾随零。
calc () {
emulate -L zsh; setopt extended_glob
local line
bc <<EOF |
scale=3
$@
EOF
while read line; do
if [[ $line = *.* ]]; then
print -r -- ${${${line%%0##}/#%0#./0}%.}
else
print -r -- $line
fi
done
}
它的编写方式更多的是通过参数替换进行文本操作的练习,而不是漂亮打印小数的真正清晰的方式。
${…%%0##}
删除最长的后缀匹配0##
,即尾随零。${…/#%0#./0}
如果字符串仅由可选前导零 ( )0
组成(#%
模式的前缀) ,则将字符串设置为${VAR/PATTERN/REPLACEMENT}
0#
.
${…%.}
删除尾随.
(如果有)。
我认为拆分步骤更清晰。
if [[ $line = *.* ]]; then line=${line%%0##}; fi
if [[ $line = . ]]; then line=0; else line=${line%.}
print -r -- $line
答案4
我们可以使用与 asm.js 相同的技术来完成此操作,而无需字符串操作或子进程:
echo $((123.45|0)) # prints 123
它利用了某些运算符(例如按位或)只能对整数执行的事实。