2つの数値の最小/最大を与えるUNIXコマンドはありますか?


37

から読み込む数値を制限するコマンドを探していましたstdin

私はその目的のために小さなスクリプトを書きました(批評は歓迎します)が、この標準的なコマンド、単純な(と思う)一般的なユースケースがないのではないかと思いました。

最小 2つの数字を見つけるスクリプト:

#!/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

回答:


20

次のdcような2つの数値を比較できます。

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連続した\newlineで区切られた数字を必要なだけ読み取って$max、woのどちらが小さいかに応じて値または入力のいずれかを出力します。

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スタックで動作します。すべての入力オブジェクトは最後にスタックされます。新しい入力オブジェクトが追加されるたびに、最後の最上位オブジェクトとその下のすべてのオブジェクトが1つずつスタックにプッシュされます。オブジェクトへのほとんどの参照は一番上のスタック値に対するものであり、ほとんどの参照はそのスタックの一番上をポップします(これにより、その下のすべてのオブジェクトが1つずつプルされます)

メインスタックの他に、(少なくとも) 256個の配列もあり、各配列要素には独自のスタックが付属しています。ここではあまり使いません。必要にl応じて文字列を保存し、必要に応じて文字列を実行できるように、文字列を保存するだけです。配列の上部の値xs引き裂き$maxましたm

とにかく、この少しdcは、主に、シェルスクリプトが行うことを行います。GNU-ism -eオプションを使用します- dc通常は標準入力からパラメータを取得しますが、次のようにすることもできます。

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

... $script上記のビットのように見えた場合。

次のように動作します:

  • lTx-このlOADSおよび電子xecutesの上に保存されたマクロT (テストのために、私は推測-私は通常、それらの名前を任意に選択)
  • z 0=?- TEST次いでWスタックの深さをテスト/ zスタックが空の場合、及び(0オブジェクトを保持する読み取り)が呼び出し?マクロ。
  • ? z0!=T q- ?マクロは? dc、stdinからの入力行を読み取る組み込みコマンドに名前が付けられていますが、別のzスタック深度テストも追加しました。これによりq、空白行を取り込むかEOFにヒットした場合に、小さなプログラム全体を実行できます。ただし!、スタックにデータが読み込まれずに正常に読み込まれた場合、Testを再度呼び出します。
  • d lm<M- TESTはその後うdスタックの上部をuplicateとそれを比較する$max (に格納されているようにmmが小さい値の場合dcMマクロを呼び出します。
  • s0 lm- Mスタックの一番上をポップしてダミーのスカラーにダンプします0-スタックをポップするだけの安価な方法です。また、est に戻る前に再びl鳴きます。mT
  • p-これはm、スタックの現在のトップよりも小さい場合、それをm置き換えdとにかくそれの複製)、ここでpリントされることを意味します。そうでない場合、そうではなく、入力がp代わりにリントされます。
  • s0-その後pスタックがポップしないため)、スタックの先頭を0再びダンプし、...
  • lTx- もう一度再帰的にl実行してから、再度実行します。Tx

そのため、この小さなスニペットを実行し、端末で対話形式で数字を入力し、入力した数字dcまたは$max入力した数字が大きかった場合の値を出力します。また、標準入力として任意のファイル(パイプなど)を受け入れます。空白行またはEOFが見つかるまで、読み取り/比較/印刷ループを継続します。

ただし、これに関するいくつかのメモ-シェル関数の動作をエミュレートするためだけにこれを書いたので、行ごとに1つの数値のみを堅牢に処理します。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?

2番目は:

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は、2回読んだ後に何が起こったのか分からないためです。これを掘り下げるのに私の時間がかかるでしょう。
ミニックス

@Minix-meh-混乱を招く場合、最も古いUnixプログラミング言語を詳しく調べる必要はありません。たぶんdc、つま先にそれを維持するために、時々いくつかの数字を時々パイプするだけかもしれません。
mikeserv

1
@mikeservそれは私には遅すぎます。将来の世代が私の注意を喚起する物語になることを願っています。どこでも角括弧と文字
...-ミニックス

@Minix-どういう意味ですか?行った?非常に良い- dc気まぐれな獣ですが、Unixシステム上で最も速く、最も奇妙に機能する共通ユーティリティである可能性があります。w / sedとペアにすると、いくつかの特別なことができます。私はそれで遊んでいます、そしてdd最近、私が怪物であることを置き換えるためにreadlineここに私がやってきたいくつかの小さなサンプルがあります。こうrevでは、dcほとんどの子供の遊びです。
mikeserv

1
@Minix-ただし、ブラケットには注意してください。文字列内に角かっこを挿入する方法はありません-できる最善の方法です[string]P91P93P[string]P。だから私はsedあなたのこの少しが便利だと思うかもしれません:sed 's/[][]/]P93]&[1P[/g;s/[]3][]][[][1[]//g'正方形を常に文字列の閉じ括弧で正しく置き換え、次にa P、次に正方形の10進数のascii値、そしてもう1つP; 次に、[文字列を続けるための開いた角括弧。w / dcの文字列/数値変換機能を台無しにしてしまった場合はDunnoですが、特にw /を組み合わせたod場合は、かなり楽しい場合があります。
mikeserv

87

2つの整数aとを処理していることがわかっている場合、三項演算子を使用したbこれらの単純なシェル算術展開は、最大値を与えるのに十分です。

$(( 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
$ 

これを示すシェルスクリプトを次に示します。

#!/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が等しい場合、どちらが返されるかは実際には関係ありません。それはあなたが求めていることですか?
デジタル外傷

本当に、ありがとう、デジタルトラウマ。ここでは、ブール演算子「> =」が可能かどうか疑問に思っていました。
ソパラホデアリエレス

@SopalajodeArrierez- if (( a >= b )); then echo a is greater than or equal to b; fiあなたが求めているのはそれですか?((( ))ここではなくの使用に注意してください$(( ))
デジタル外傷

ああ、はい、OK。今、私は分かる。シェルの拡張についてはあまり知らないので、通常は条件間で混乱します。どうもありがとう。
ソパラホデアリエレス

24

sortそしてheadこれを行うことができます。

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ただし、2つのプロセスの生成はオーバーヘッドがはるかに大きいため、重要性はほとんどありません。
ライライアン

1
本当ですが、@ glenn-jackman、質問を考えればそれが重要かどうかはわかりません。最も効率的な方法を求める要求はありませんでした。質問は利便性に関するものだったと思います。
デビッドHoelzer

1
@DavidHoelzer-これは、ここで提供される回答の中でも、これを行う最も効率的な方法ではありません。数値セットで作業する場合、これよりも効率的な少なくとも1つの他の答えがここにあり(大きさで)、2つの整数で作業する場合、それよりも効率的な別の答えがあります(大きさで)。しかし、それは便利です(ただし、おそらく個人的にはシェル配列を省略します)
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

間違っていなければ、必要に応じて、そのような関数を実行可能ファイルとともにコンパイルすることもできます。しかし、GNUがもはやそのようなものではなくなったとしても、ほとんどbcのsは今日のdcフロントエンドにすぎないと思います(ただし、GNU とGNU はコードベースの膨大な量を共有しています)。とにかく、これがここでのベストアンサーかもしれません。bcdcbc
mikeserv

シェルスクリプトのファイル内でこれを便利に呼び出すbcには、関数呼び出しの直前に関数定義をパイプすることもできます。2番目のファイルは必要ありません:)
タニウス

5

コメントが長すぎます:

これらのことは、たとえば、sort | headまたはsort | tailコンボを使用して行うことができますが、リソース処理とエラー処理の両方の面でやや最適とは言えません。実行に関する限り、コンボは2行をチェックするためだけに2つのプロセスを生成することを意味します。それはちょっとやり過ぎのようです。

より深刻な問題は、ほとんどの場合、入力が正しかったこと、つまり数字のみが含まれていることを知る必要があるということです。@glennjackmannのソリューションは、これを巧妙に解決しprintf %dます。非整数をバーフする必要があるからです。浮動小数点でも機能しません(フォーマット指定子をに変更しない限り%f、丸めの問題が発生します)。

test $1 -gt $2 比較が失敗したかどうかを示します(終了ステータス2は、テスト中にエラーが発生したことを意味します。通常、これはシェルに組み込まれているため、追加のプロセスは生成されません。ただし、整数のみで動作します。

いくつかの浮動小数点数を比較する必要がある場合、興味深いオプションはbc次のとおりです。

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

はに相当test $1 -gt $2し、シェルでinを使用します。

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

それでもprintf | sort | head(2つの数値の場合)よりもほぼ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はあまり見られません。
ペテルフ

逆ポーランド記法(別名Postfix記法)。さらにdc、I / Oを独自に実行できる場合、それよりもさらにエレガントになります。
ペテルフ

4

$ aと$ bの大きな値を取得するには、これを使用します。

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

しかし、あなたはその周りに何かが必要です、あなたはおそらく数を実行するつもりはないので、2つの大きな値を表示するには「エコー」を使用してください

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

上記はシェル関数にうまく適合します。例えば

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

2つのうち大きい方を変数に割り当てるには、次の修正バージョンを使用します。

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

または、定義された関数を使用します。

biggest=$( max $a $b )

関数のバリエーションにより、入力エラーのチェックをきちんと追加することもできます。

使用できる最大2つの10進数/浮動小数点数を返すには awk

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

編集:このテクニックを使用すると、編集/メモに従って逆方向に動作する「制限」機能を作成できます。この関数は、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構造が多数ある場合、スクリプトは急速に拡張されます。

単一のスクリプトでより大きな/小さな数のチェックを複数回再利用したい場合は、関数に入れてください。関数形式を使用すると、デバッグと再利用が容易になり、スクリプトのその部分を簡単に置き換えることができます。たとえば、整数以外の10進数を処理できるようにawkコマンドを使用できます。

単一のユースケースの場合は、インラインでコーディングするだけです。


4

次のように関数を定義できます

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

として呼び出して、maxnum 54 42エコーし54ます。必要に応じて、関数内に検証情報(引数として2つの引数や数値など)を追加できます。


ほとんどのシェルは浮動小数点演算を行いません。ただし、整数では機能します。
オリオン

1
この関数は不必要にPOSIXと互換性がありません。に変更function maxnum {するmaxnum() {と、はるかに多くのシェルで機能します。
チャールズダフィー

2

シェルスクリプトから、任意の 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'または同様の。

シェルは数値処理にはまったく理想的ではありませんが、より良い他のプログラムに簡単にパイプすることができます。あなた自身の好みに応じて、max call dc(少し難読化されていますが、あなたが何をしているのか知っているなら、それは大丈夫です-mikeservの答えを見てください)、またはawk 'NR==1{max=$1} {if($1>max){max=$1}} END { print max }'。または多分perlまたはpythonあなたが好めば。1つの解決策(あまり知られていないソフトウェアをインストールして使用する場合)はised(特に、データが1行である場合:行う必要があるだけですised --l input.dat 'max$1')。


2つの数字を要求しているため、これはすべてやり過ぎです。これで十分です:

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

1
あなたが使用した場合より良いかもしれませんsys.argvpython2 -c 'import sys; print (max(sys.argv))' "$@"
muru

1
sort + headやり過ぎではあるが、python計算されない引数。
mikeserv

行の上のすべてのメソッドは、膨大な数のセットを処理し、この種の使用(パイプまたはファイルから読み取る)を明示的に提案するように設計されています。2つの引数のmin / maxは異なる感じの質問です。ストリームではなく関数を呼び出します。私はただストリームのアプローチが過剰であることを意味しました-あなたが使用するツールはarbitrary python意的です。
オリオン

私はこの提案された解決策をよりすてきなものと呼びますが、それは私がpython偏執者であるためかもしれません(またはフォークと追加の巨大なインタプリタを必要としないため)。または多分両方。
mikeserv

@mikeserv整数であることがわかっていれば、それも使用します。私が言及したこれらのすべてのソリューションは、数値が浮動小数点数である可能性があることを前提としています-bashは浮動小数点を使用せず、zshがネイティブシェルでない限り、フォーク(およびナイフ)必要になります。
オリオン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.