是否有unix命令给出两个数字的最小值/最大值?


37

我正在寻找一个命令来限制从中读取的数字stdin

我为此目的编写了一个小脚本(欢迎批评),但我想知道是否没有针对此简单且(我认为)常见用例的标准命令。

我的脚本找到了至少两个数字:

#!/bin/bash
# $1 limit

[ -z "$1" ] && { echo "Needs a limit as first argument." >&2; exit 1; }

read number

if [ "$number" -gt "$1" ]; then
        echo "$1"
else
        echo "$number"
fi

Answers:


20

您可以只比较两个数字,dc例如:

dc -e "[$1]sM $2d $1<Mp"

... "$1"最大值"$2"是哪里,如果小于,则是要打印的数字"$1"。这也需要GNU- dc但您可以轻而易举地做同样的事情:

dc <<MAX
    [$1]sM $2d $1<Mp
MAX

在上述两种情况下,你可以设置精度为0以外的东西(默认值)类似${desired_precision}k。对于这两者,还必须确保两个值都绝对是数字,因为dc可以system()通过!操作员进行调用。

使用下面的小脚本(以及下一个小脚本),您还应该验证输入-类似grep -v \!|dc或类似的东西来可靠地处理任意输入。您还应该知道,它dc_前缀而不是-前缀来解释负数-因为后者是减法运算符。

除此之外,此脚本dc将读取\n您希望提供的按顺序的双行分隔的数字,并为您的$max值或输入中的每一个打印输出,具体取决于哪一个值较小:

dc -e "${max}sm
       [ z 0=? d lm<M p s0 lTx ]ST
       [ ? z 0!=T q ]S?
       [ s0 lm ]SM lTx"

所以...每个那些的[方括号]广阔是一个dc 字符串是对象S的每个AVED到其各自的阵列-中的任何一个T?M。除了字符串dc可以做的一些其他事情外,它还可以将宏作为一个宏来退出。如果您安排得当,则可以简单地组装一个功能齐全的小脚本。xdc

dc堆栈上工作。所有输入对象都堆叠在最后一个对象上-每个新输入对象将添加的最后一个顶部对象及其下面的所有对象向下推一推。对对象的大多数引用都指向栈顶值,并且大多数引用将栈顶弹出(这会将其下的所有对象向上拉一位)

除主堆栈外,还(至少)有 256个数组,每个数组元素都带有一个自己的堆栈。我在这里用的不多。我只是按上述方式存储了字符串,所以我可以l在需要时对其进行x遍历并有条件地执行它们,然后s$max值撕裂到m数组顶部。

无论如何,这一点都可以dc完成您的shell脚本。它确实使用了GNU-ism -e选项- dc通常从standard-in中获取其参数-但您可以执行以下操作:

echo "$script" | cat - /dev/tty | dc

...如果$script看起来像上面的位。

它的工作原理如下:

  • lTx-此l操作x可将存储在顶部的宏删除T (为了测试,我想-我通常会随意选择这些名称)
  • z 0=?- TEST然后测试堆栈深度瓦特/ z和,如果堆栈是空的(读:保持为0的对象),它调用?宏。
  • ? z0!=T q-该?宏以? dc内置命令命名,该命令从stdin读取一行输入,但是我还z向其添加了另一个堆栈深度测试,这样,q如果它拉进空行或命中EOF ,它就可以使用整个小程序。但是,如果!没有,而是成功地填充了堆栈,它将T再次调用est。
  • d lm<M- TEST然后将duplicate堆栈的顶部,并比较它$max (存储在m。如果m是较小的值,则dc调用M宏。
  • s0 lm- M只是弹出堆栈的顶部并将其转储到虚拟标量0-只是弹出堆栈的一种廉价方法。在返回est 之前,它也会再次l覆盖。mT
  • p-这意味着,如果m小于当前堆栈的顶部,则将其m替换d无论如何是它的uplicate)并在此处进行p漂洗,否则不进行漂洗,而是清除任何输入p
  • s0-之后(因为p不会弹出堆栈),我们0再次将堆栈的顶部转储到其中,然后...
  • lTx-递归lOAD TEST一次则E x再次ecute它。

因此,您可以在终端上运行此小片段并以交互方式键入数字,然后dc将输入的数字或$max键入的数字较大时的值打印回给您。它还将接受任何文件(例如管道)作为标准输入。它将继续读取/比较/打印循环,直到遇到空白行或EOF。

不过,有些注意事项-我编写此代码只是为了模拟您的shell函数中的行为,因此它仅能可靠地处理每行一个数字。dc但是,您可以处理每行尽可能多的空格分隔数字。但是,由于它的堆栈,所以最后一行上的最后一个数字是它运行的第一个数字,因此,按书面要求,dc如果在每行上打印/键入多个数字,则将反向打印其输出。处理就是将一行存储在数组中,然后对其进行处理。

像这样:

dc -e "${max}sm
    [ d lm<M la 1+ d sa :a z0!=A ]SA
    [ la d ;ap s0 1- d sa 0!=P ]SP 
    [ ? z 0=q lAx lPx l?x ]S?
    [q]Sq [ s0 lm ]SM 0sa l?x"

但是...我不知道是否要深入解释。只需说一下,当dc读取堆栈中的每个值时,它就将其值或$max的值存储在索引数组中,一旦它检测到堆栈再次为空,则在尝试读取另一个索引对象之前,它会打印每个索引对象输入行。

因此,尽管第一个脚本执行了...

10 15 20 25 30    ##my input line
20
20
20
15
10                ##see what I mean?

第二个是:

10 15 20 25 30    ##my input line
10                ##that's better
15
20
20                ##$max is 20 for both examples
20

如果首先使用k命令设置浮点数,则可以处理任意精度的浮点数。并且您可以独立地更改input或output半径-有时由于您可能不希望的原因而很有用。例如:

echo 100000o 10p|dc
 00010

...首先将dc输出基数设置为100000,然后打印10。


3
+1表示不知道两次阅读后会发生什么。将不得不花我的时间去研究这一点。
Minix

@Minix-嗯-如果您发现最令人困惑的Unix语言,则无需深入研究。也许只是dc每隔一段时间就传送一些数字,以使其保持脚尖。
mikeserv

1
@mikeserv对我来说太晚了。我希望后代将我作为一个警告。方括号和字母无处不在...
Minix 2015年

@Minix-您是什么意思?你去了吗?很好- dc是善变的野兽,但是它可能只是任何Unix系统上最快,最奇怪的功能。与w配对时,sed它可以做一些非凡的事情。我一直在玩它,dd最近才可以代替那只怪兽readline这是我一直在做的一些小事。做一个revdc几乎是儿戏。
mikeserv

1
@Minix-小心带括号。无法在字符串中放置方括号-最好的办法是[string]P91P93P[string]P。因此,我sed可能对您有帮助:sed 's/[][]/]P93]&[1P[/g;s/[]3][]][[][1[]//g'这应该始终使用字符串右括号,然后是a P,然后是正方形的十进制ascii值以及另一个来正确替换正方形P。然后用[方括号将字符串继续。Dunno如果您搞砸了w / dc的字符串/数字转换功能,但是-特别是当w /组合使用时od-可能会很有趣。
mikeserv

88

如果您知道要处理两个整数ab,那么使用三元运算符进行的这些简单的shell算术展开足以给出数值最大值:

$(( a > b ? a : b ))

和数字最小值:

$(( a < b ? a : b ))

例如

$ a=10
$ b=20
$ max=$(( a > b ? a : b ))
$ min=$(( a < b ? a : b ))
$ echo $max
20
$ echo $min
10
$ a=30
$ max=$(( a > b ? a : b ))
$ min=$(( a < b ? a : b ))
$ echo $max
30
$ echo $min
20
$ 

这是一个shell脚本来演示:

#!/usr/bin/env bash
[ -z "$1" ] && { echo "Needs a limit as first argument." >&2; exit 1; }
read number
echo Min: $(( $number  < $1 ? $number : $1 ))
echo Max: $(( $number  > $1 ? $number : $1 ))

好答案。拜托,一个小的mod:也可以用于“> =”吗?
索帕拉霍·德·阿里埃雷斯

@SopalajodeArrierez我不确定您的意思。您也可以这样做max=$(( a >= b ? a : b )),但结果完全相同-如果a和b相等,那么返回哪个返回值并不重要。那是你要的吗
Digital Trauma

确实,谢谢你,数字创伤。我只是想知道布尔运算符“> =”是否可以在这里使用。
Sopalajo de Arrierez,2016年

@SopalajodeArrierez- if (( a >= b )); then echo a is greater than or equal to b; fi这是您要的吗?(请注意使用(( ))此处代替$(( ))
数字创伤

是的,好的。我现在知道了。我对shell扩展了解不多,所以我通常在条件之间感到困惑。再次谢谢你。
Sopalajo de Arrierez '16

25

sorthead可以这样做:

numbers=(1 4 3 5 7 1 10 21 8)
printf "%d\n" "${numbers[@]}" | sort -rn | head -1       # => 21

2
请注意,这是O(n log(n))max的有效实现O(n)。但是,这几乎没有什么意义n=2,因为产生两个进程的开销要大得多。
Lie Ryan

1
@ glenn-jackman是真实的,但我不确定这个问题是否重要。没有要求采用最有效的方法。我认为问题更多是关于便利性。
David Hoelzer

1
@DavidHoelzer-即使在此处提供答案,这也不是最有效的方法。如果使用数字集,则在此至少有另一个答案要比这个效率更高(按数量级);如果仅使用两个整数,则这里的另一个答案要比按此数量级效率更高(按数量级)。虽然很方便(但是我个人可能会省略shell数组)
mikeserv

1
可以在不使用数组的情况下完成以下操作:numbers="1 4 3 5 7 1 10 21 8"; echo $numbers | tr ' ' "\n" | sort -rn | head -n 1
ngreen

1
一种更有效的方法可能是:max=0; for x in $numbers ; do test $x -gt $max && max=$x ; done
ngreen

6

您可以为其定义预定义的数学函数库,bc然后在命令行中使用它们。

例如,在文本文件中包括以下内容~/MyExtensions.bc

define max(a,b){
  if(a>b)
  { 
   return(a)
  }else{
   return(b)
  }
}

现在您可以致电bc

> echo 'max(60,54)' | bc ~/MyExtensions.bc
60

仅供参考,在线有免费的数学库函数,例如该函数。

使用该文件,您可以轻松计算出更复杂的函数,例如GCD

> echo 'gcd (60,54)' | bc ~/extensions.bc -l
6

如果我没记错的话,必要时还可以将这些函数与可执行文件一起编译。我认为大多数bcs只是今天的dc前端,即使GNUbc不再如此(但是GNU dc和GNU bc共享了大量的代码库)。无论如何,这可能是最好的答案。
mikeserv

为了方便地在Shell脚本的文件中调用此函数,您也可以在bc函数调用之前将函数定义也传递到。那么就不需要第二个文件了:)
tanius

5

评论太久了:

尽管您可以使用sort | headsort | tail组合来完成这些操作,但在资源和错误处理方面似乎都不是很理想。就执行而言,组合意味着产生2个进程只是为了检查两行。这似乎有些过大。

更严重的问题是,在大多数情况下,您需要知道输入是理智的,仅包含数字。@glennjackmann的解决方案巧妙地解决了这个问题,因为printf %d应该禁止使用非整数。它也不适用于浮点数(除非您将格式说明符更改为%f,否则会遇到舍入问题)。

test $1 -gt $2 会向您指示比较是否失败(退出状态2表示测试期间发生错误。由于这通常是内置的shell,因此不会产生其他进程-我们所说的是数百个命令执行速度提高了两倍,但仅适用于整数。

如果您碰巧需要比较几个浮点数,则有趣的选择可能是bc

define x(a, b) {
    if (a > b) {
       return (a);
    }
    return (b);
 }

将等同于test $1 -gt $2,并在shell中使用:

max () { printf '
    define x(a, b) {
        if (a > b) {
           return (a);
        }
        return (b);
     }
     x(%s, %s)
    ' $1 $2 | bc -l
}

仍然比printf | sort | head(两个数字)快2.5倍。

如果您可以依赖中的GNU扩展bc,那么您还可以使用该read()函数将数字直接读入bc清单。


我的想法完全是-我只是在解决这个问题,但是您击败了我:dc -e "${max}sm[z0=?dlm<Mps0lTx]ST[?z0!=Tq]S?[s0lm]SMlTx"-哦,除了dc整个事情(回声除外,尽管可以) -它会读取stdin并$max根据其中的一个打印输出或输入数字较小。无论如何,我不太在乎解释它,而且您的答案比我要写的要好。所以请给我投票。
mikeserv

@mikeserv实际上有一个解释dc好的脚本会非常好,这些天很少看到RPN。
彼得,2015年

反向波兰语符号(又名后缀符号)。另外,如果dc能够自己完成I / O,那将比它更加优雅。
彼得,2015年

4

要获得$ a和$ b的较大值,请使用以下命令:

[ "$a" -gt "$b" ] && $a || $b

但是您需要解决此问题,您可能不需要执行该数字,因此要使用“ echo”显示两者中的较大值

[ "$a" -gt "$b" ] && echo $a || echo $b

上面的代码非常适合shell函数,例如

max() {
   [ "$1" -gt "$2" ] && echo $1 || echo $2
}

要将两者中较大的一个分配给变量,请使用以下修改版本:

[ "$a" -gt "$b" ] && biggest=$a || biggest=$b

或使用定义的功能:

biggest=$( max $a $b )

功能的变化还使您有机会巧妙地添加输入错误检查。

要返回两个小数/浮点数的最大值,可以使用 awk

decimalmax() { 
   echo $1 $2 | awk '{if ($1 > $2) {print $1} else {print $2}}'; 
}

编辑:使用此技术,您可以创建一个“限制”功能,该功能可以按照您的编辑/注释进行其他操作。此函数将返回两者中的较低者,例如:

limit() {
   [ "$1" -gt "$2" ] && echo $2 || echo $1
}

我喜欢将实用程序函数放入一个单独的文件中,进行调用myprogram.funcs并在脚本中使用它,如下所示:

#!/bin/bash

# Initialization. Read in the utility functions
. ./myprogram.funcs

# Do stuff here
#
[ -z "$1" ] && { echo "Needs a limit as first argument." >&2; exit 1; }

read number
echo $( limit $1 $number )

FWIW仍然可以完成您的操作,即使您的版本更详细,它的版本也同样有效。

更为紧凑的形式并不一定会更好,但是可以防止脚本混乱。如果您有许多简单的if-then-else-fi构造,脚本将迅速扩展。

如果要在单个脚本中多次重复使用大号或小号支票,请将其放入函数中。函数格式使调试和重用更加容易,并且允许您轻松替换脚本的该部分,例如,使用awk命令可以处理非整数十进制数字。

如果是单个用例,则只需内联代码即可。


4

您可以将函数定义为

maxnum(){
    if [ $2 -gt $1 ]
    then
        echo $2
    else
        echo $1
    fi
}

称它为maxnum 54 42,它回声54。如果愿意,可以在函数内添加验证信息(例如两个参数或数字作为参数)。


大多数壳不执行浮点运算。但是它确实适用于整数。
Orion

1
此功能不必要地与POSIX不兼容。更改function maxnum {maxnum() {,它将适用于更多的shell。
查尔斯·达菲

2

通过shell脚本,可以使用任何 Java公共静态方法(例如Math.min())。在Linux上的bash中:

. jsbInit
jsbStart 
A=2 
B=3 
C=$(jsb Math.min "$A" "$B")
echo "$C"

这需要Java Shell Bridge https://sourceforge.net/projects/jsbridge/

非常快,因为方法调用是内部管道传递的;无需任何过程。


0

大多数人只会做sort -n input | head -n1(或尾巴),对于大多数脚本编写情况来说已经足够了。但是,如果您在一行而不是一列中有数字,这会有些笨拙-您必须以正确的格式(tr ' ' '\n'或类似格式)将其打印出来。

Shell并非完全适合于数值处理,但是您可以轻松地将其传递到其他更好的程序中。根据您自己的喜好,您可以拨打max电话dc(有点混乱,但是如果您知道自己在做什么,那很好-请参阅mikeserv的回答)或awk 'NR==1{max=$1} {if($1>max){max=$1}} END { print max }'。或者可能,perl或者python您愿意。一种解决方案(如果您愿意安装和使用鲜为人知的软件)ised(特别是如果您的数据位于一行中:您只需要这样做ised --l input.dat 'max$1')。


因为您要输入两个数字,所以这太过分了。这应该足够了:

python -c "print(max($j,$k))"

1
如果使用过,可能会更好sys.argvpython2 -c 'import sys; print (max(sys.argv))' "$@"
muru 2015年

1
过大sort + headpython不能计算的参数不会计算。
mikeserv

该行上方的所有方法均旨在处理大量数字,并明确建议这种使用方式(从管道或文件中读取)。2个参数的min / max是一个不同的问题-它要求使用函数而不是流。我只是说流方法过大-您使用的工具是任意的,我python之所以使用是因为它很简洁。
Orion 2015年

我称这种建议的解决方案更整洁,但这可能是因为我很python顽固(或者因为它不需要fork和额外的巨大解释器)。或两者兼而有之。
mikeserv

@mikeserv如果我知道它们是整数,我也会使用它。我提到的所有这些解决方案都是在假设数字可能是浮点数的情况下进行的-bash不会进行浮点运算,除非zsh是您的本机外壳,否则您需要一把叉子(可能还有一把刀)。
Orion
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.