it-swarm.com.de

Gibt es einen Unix-Befehl, der das Minimum / Maximum von zwei Zahlen angibt?

Ich suchte nach einem Befehl zum Begrenzen der von stdin eingelesenen Zahlen.

Zu diesem Zweck habe ich ein kleines Skript geschrieben (Kritik ist willkommen), aber ich habe mich gefragt, ob es für diesen einfachen und (glaube ich) allgemeinen Anwendungsfall keinen Standardbefehl gibt.

Mein Skript, das das Minimum von zwei Zahlen findet:

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

Sie können nur zwei Zahlen mit dc vergleichen, wie:

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

... wobei "$1" Ihr Maximalwert ist und "$2" die Zahl ist, die Sie drucken würden, wenn sie kleiner als "$1" wäre. Das erfordert auch GNU dc - aber Sie können das Gleiche portabel tun wie:

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

In beiden oben genannten Fällen können Sie die Genauigkeit auf etwas anderes als 0 (Standardeinstellung) wie ${desired_precision}k Setzen. Für beide ist es auch unerlässlich, dass Sie überprüfen, ob beide Werte definitiv Zahlen sind, da dcsystem() Aufrufe ausführen kann mit dem Operator !.

Mit dem folgenden kleinen Skript (und dem nächsten) sollten Sie auch die Eingabe überprüfen - wie grep -v \!|dc Oder etwas, um willkürliche Eingaben robust zu handhaben . Sie sollten auch wissen, dass dc negative Zahlen mit einem Präfix _ Anstelle eines Präfixes - Interpretiert - da letzteres der Subtraktionsoperator ist.

Abgesehen davon liest dc mit diesem Skript so viele fortlaufende \n Ewline-getrennte Zahlen ein, wie Sie möchten, und druckt für jede entweder Ihren $max - Wert oder die Eingabe, je nachdem welche die geringere der wo ist:

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

Also ... jede dieser [ In eckigen Klammern ] Erweitert ist ein dc String Objekt Das heißt, S wird jeweils in das jeweilige Array eingefügt - eines von T, ? oder M. Neben einigen anderen Dingen, die dc mit einem String tun könnte, kann es auch einen x als Makro ausführen . Wenn Sie es richtig arrangieren, wird ein voll funktionsfähiges kleines dc Skript einfach genug zusammengestellt.

dc funktioniert auf einem Stapel . Alle Eingabeobjekte werden jeweils auf das letzte gestapelt - jedes neue Eingabeobjekt drückt das letzte obere Objekt und alle darunter liegenden Objekte beim Hinzufügen um eins auf den Stapel. Die meisten Verweise auf ein Objekt beziehen sich auf den obersten Stapelwert, und die meisten Verweise öffnen den obersten Stapel (der gezogen wird) alle Objekte darunter um eins) .

Neben dem Hauptstapel gibt es auch (mindestens) 256 Arrays, und jedes Array-Element verfügt über einen eigenen Stapel. Davon verwende ich hier nicht viel. Ich speichere die Zeichenfolgen einfach wie erwähnt, damit ich sie bei Bedarf l laden und x bedingt ausführen kann, und ich s habe den Wert von $max Gerissen oben im Array m.

Wie auch immer, dieses kleine Stück dc macht weitgehend das, was Ihr Shell-Skript macht. Es wird zwar die Option GNU-ism -e Verwendet, da dc seine Parameter im Allgemeinen vom Standard-In übernimmt. Sie können jedoch Folgendes tun:

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

... wenn $script wie das obige Bit aussah.

Es funktioniert wie:

  • lTx - Diese l lädt und e x führt das Makro aus, das oben in T gespeichert ist (zum Testen, Ich denke - ich wähle diese Namen normalerweise willkürlich aus) .
  • z 0=? - T est testet dann die Stapeltiefe mit z und, wenn der Stapel leer ist (gelesen: enthält 0 Objekte) ruft das Makro ? Auf.
  • ? z0!=T q - Das Makro ? Ist nach dem eingebauten Befehl ?dc benannt, der eine Eingabezeile von stdin liest, aber ich habe auch ein weiteres z Stapeltiefen-Test, so dass es q für das gesamte kleine Programm geeignet ist, wenn es eine leere Zeile zieht oder EOF trifft. Wenn dies jedoch nicht der Fall ist ! Und stattdessen den Stapel erfolgreich gefüllt wird, wird erneut T est aufgerufen.
  • d lm<M - T est wird dann d die Oberseite des Stapels erhöhen und mit $max vergleichen (wie in gespeichert) m) . Wenn m der kleinere Wert ist, ruft dc das Makro M auf.
  • s0 lm - M legt nur den oberen Teil des Stapels ab und legt ihn auf dem Dummy-Skalar ab 0 - nur eine kostengünstige Methode, um den Stapel zu öffnen. Außerdem wird l erneut m geladen, bevor zu T est zurückgekehrt wird.
  • p - Dies bedeutet, dass wenn m kleiner als die aktuelle Stapelspitze ist, m diese ersetzt (die d uplicate davon sowieso) und ist hier p gedruckt, sonst nicht und was auch immer die Eingabe war, wird stattdessen p gedruckt.
  • s0 - Danach (weil p den Stapel nicht öffnet) werfen wir den oberen Teil des Stapels in 0 Wieder und dann ...
  • lTx - rekursiv l lade T noch einmal, dann führe es x erneut aus.

Sie könnten also dieses kleine Snippet ausführen und interaktiv Zahlen an Ihrem Terminal eingeben, und dc würde entweder die von Ihnen eingegebene Nummer oder den Wert von $max An Sie zurückdrucken, wenn die von Ihnen eingegebene Nummer größer wäre. Es würde auch jede Datei (wie eine Pipe) als Standardeingabe akzeptieren. Die Lese-/Vergleichs-/Druckschleife wird fortgesetzt, bis eine leere Zeile oder ein EOF auftritt.

Einige Anmerkungen dazu - Ich habe dies geschrieben, um das Verhalten in Ihrer Shell-Funktion zu emulieren, sodass nur eine Zahl pro Zeile zuverlässig verarbeitet wird. dc kann jedoch so viele durch Leerzeichen getrennte Zahlen pro Zeile verarbeiten, wie Sie möchten. Allerdings wird aufgrund seines Stapels die letzte Zahl in einer Zeile die erste sein, mit der sie arbeitet, und so, wie geschrieben, dc würde seine Ausgabe in umgekehrter Reihenfolge drucken, wenn Sie mehr als eine Zahl pro Zeile gedruckt/eingegeben hätten. Die richtige Vorgehensweise besteht darin, eine Zeile in einem Array zu speichern und dann zu bearbeiten.

So was:

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"

Aber ... ich weiß nicht, ob ich das so ausführlich erklären will. Es genügt zu sagen, dass beim Einlesen jedes Werts auf dem Stapel durch dc entweder sein Wert oder der Wert von $max In einem indizierten Array gespeichert wird und der Stapel, sobald er erkannt wird, wieder leer ist Anschließend wird jedes indizierte Objekt gedruckt, bevor versucht wird, eine weitere Eingabezeile zu lesen.

Und so, während das erste Skript ...

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

Der zweite macht:

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

Sie können Floats mit beliebiger Genauigkeit verarbeiten, wenn Sie sie zuerst mit dem Befehl k festlegen. Und Sie können die Eingaberadices i nput oder o unabhängig voneinander ändern - was manchmal aus Gründen nützlich sein kann, die Sie möglicherweise nicht erwarten. Zum Beispiel:

echo 100000o 10p|dc
 00010

... der zuerst den Ausgangsradix von dc auf 100000 setzt und dann 10 druckt.

20
mikeserv

Wenn Sie wissen, dass Sie es mit zwei ganzen Zahlen a und b zu tun haben, reichen diese einfachen Shell-arithmetische Erweiterungen mit dem ternären Operator aus, um das numerische Maximum zu ergeben:

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

und numerische min:

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

Z.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
$ 

Hier ist ein Shell-Skript, das dies demonstriert:

#!/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 ))
97
Digital Trauma

sort und head können dies tun:

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

Sie können eine Bibliothek vordefinierter mathematischer Funktionen für bc definieren und diese dann in der Befehlszeile verwenden.

Fügen Sie beispielsweise Folgendes in eine Textdatei wie ~/MyExtensions.bc:

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

Jetzt können Sie bc aufrufen durch:

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

Zu Ihrer Information, es gibt kostenlose Funktionen der Mathematikbibliothek wie diese online verfügbar.

Mit dieser Datei können Sie leicht kompliziertere Funktionen wie GCD berechnen:

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

Zu lang für einen Kommentar:

Während Sie diese Dinge tun können, z. Mit den Kombinationen sort | head oder sort | tail scheint es sowohl in Bezug auf die Ressourcen als auch in Bezug auf die Fehlerbehandlung eher suboptimal zu sein. In Bezug auf die Ausführung bedeutet die Kombination, dass zwei Prozesse erzeugt werden, um nur zwei Zeilen zu überprüfen. Das scheint ein bisschen übertrieben zu sein.

Das schwerwiegendere Problem ist, dass Sie in den meisten Fällen wissen müssen, dass die Eingabe vernünftig ist, dh nur Zahlen enthält. @ glennjackmanns Lösung löst dies geschickt, da printf %d auf Nicht-Ganzzahlen verzichten sollte. Es funktioniert auch nicht mit Floats (es sei denn, Sie ändern den Formatbezeichner in %f, Wo Rundungsprobleme auftreten).

test $1 -gt $2 Gibt Ihnen einen Hinweis darauf, ob der Vergleich fehlgeschlagen ist oder nicht (Exit-Status 2 bedeutet, dass während des Tests ein Fehler aufgetreten ist. Da dies normalerweise eine integrierte Shell ist, wird kein zusätzlicher Prozess erzeugt - wir ' Ich spreche von einer Reihenfolge, die hundertmal schneller ausgeführt wird. Funktioniert jedoch nur mit ganzen Zahlen.

Wenn Sie zufällig einige Gleitkommazahlen vergleichen müssen, ist die interessante Option möglicherweise bc:

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

wäre das Äquivalent von test $1 -gt $2 und die Verwendung in in Shell:

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

ist immer noch fast 2,5 mal schneller als printf | sort | head (für zwei Zahlen).

Wenn Sie sich auf GNU -Erweiterungen in bc verlassen können, können Sie auch die Funktion read() verwenden, um die Zahlen direkt in bc einzulesen sript.

5
peterph

Sie können eine Funktion als definieren

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

Nennen Sie es als maxnum 54 42 und es hallt 54. Sie können Validierungsinformationen innerhalb der Funktion hinzufügen (z. B. zwei Argumente oder Zahlen als Argumente), wenn Sie möchten.

4
unxnut

Um den höheren Wert von $ a und $ b zu erhalten, verwenden Sie Folgendes:

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

Aber Sie brauchen etwas um das herum, Sie wollen wahrscheinlich nicht die Zahl ausführen, also, um den größeren Wert der beiden anzuzeigen, verwenden Sie "Echo".

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

Das Obige passt gut in eine Shell-Funktion, z

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

Verwenden Sie diese geänderte Version, um der Variablen den größeren der beiden zuzuweisen:

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

oder verwenden Sie die definierte Funktion:

biggest=$( max $a $b )

Die Funktionsvariante bietet Ihnen auch die Möglichkeit, die Eingabefehlerprüfung übersichtlich hinzuzufügen.

Um das Maximum von zwei Dezimal-/Gleitkommazahlen zurückzugeben, können Sie awk verwenden

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

BEARBEITEN: Mit dieser Technik können Sie eine "Limit" -Funktion erstellen, die gemäß Ihrer Bearbeitung/Notiz umgekehrt funktioniert. Diese Funktion gibt den unteren der beiden zurück, z.

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

Ich möchte Dienstprogrammfunktionen in eine separate Datei einfügen, nenne es myprogram.funcs und verwenden Sie es in einem Skript wie folgt:

#!/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 dies macht immer noch das, was Sie getan haben, und Ihre Version ist, obwohl sie ausführlicher ist, genauso effizient.

Die kompaktere Form ist nicht wirklich besser, verhindert jedoch Unordnung in Ihren Skripten. Wenn Sie viele einfache if-then-else-fi-Konstrukte haben, wird das Skript schnell erweitert.

Wenn Sie die Prüfung für größere/kleinere Zahlen in einem einzigen Skript mehrmals wiederverwenden möchten, fügen Sie sie in eine Funktion ein. Das Funktionsformat erleichtert das Debuggen und die Wiederverwendung und ermöglicht es Ihnen, diesen Teil des Skripts einfach zu ersetzen, beispielsweise durch einen Befehl awk, um nicht ganzzahlige Dezimalzahlen verarbeiten zu können.

Wenn es sich um einen einzelnen Anwendungsfall handelt, codieren Sie ihn einfach inline.

4
Johan

In einem Shell-Skript gibt es eine Möglichkeit, eine beliebige Java öffentliche statische Methode (und zum Beispiel Math. min () ). Von Bash unter Linux:

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

Dies erfordert Java Shell Bridge https://sourceforge.net/projects/jsbridge/

Sehr schnell, da die Methodenaufrufe intern geleitet werden ; Kein Prozess erforderlich.

2
Fil

Die meisten Leute würden nur sort -n input | head -n1 (Oder Tail) machen, es ist gut genug für die meisten Skriptsituationen. Dies ist jedoch etwas umständlich, wenn Sie Zahlen in einer Zeile anstelle einer Spalte haben - Sie müssen sie in einem geeigneten Format (tr ' ' '\n' Oder ähnlichem) ausdrucken.

Shells sind nicht gerade ideal für die numerische Verarbeitung, aber Sie können sie einfach in ein anderes Programm einbinden, das darin besser ist. Abhängig von Ihrer eigenen Präferenz rufen Sie maximal dc (etwas verschleiert, aber wenn Sie wissen, was Sie tun, ist es in Ordnung - siehe die Antwort von mikeserv) oder awk 'NR==1{max=$1} {if($1>max){max=$1}} END { print max }' auf. Oder möglicherweise Perl oder python, wenn Sie es vorziehen. Eine Lösung (wenn Sie bereit sind, weniger bekannte Software zu installieren und zu verwenden) wäre ised (insbesondere, wenn Ihre Daten in einer einzigen Zeile stehen: Sie müssen nur ised --l input.dat 'max$1' Ausführen).


Da Sie nach zwei Zahlen fragen, ist das alles übertrieben. Das sollte reichen:

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