Wie schreibe ich ein Shell-Skript, um numerischen Bereichen Buchstabenstufen zuzuweisen?

19

Ich möchte ein Skript erstellen, das Sie zur Eingabe einer Zahl zwischen 0 und 100 auffordert, und Ihnen dann die Note basierend auf der Zahl geben.

Ich würde es gerne in bash.

PS3='Please enter your choice: '
(Something here)

do
case $
    "0-59")
        echo "F"
        ;;
    "60-69")
        echo "D"
        ;;
    "70-79")
        echo "C"
        ;;
    "Quit")
        break
        ;;
    *) echo invalid option;;
esac
done
Temple Pate
quelle

Antworten:

20

Kürze vs. Lesbarkeit: Ein Mittelweg

Wie Sie gesehen haben, ist dieses Problem bekannt für Lösungen, die mäßig lang sind und sich etwas wiederholen, aber gut lesbar sind ( Antworten von Terdon und AB ) sowie für Lösungen, die sehr kurz, aber nicht intuitiv und viel weniger selbstdokumentierend sind (Tims Python) und bash answers und glenn jackmans perl answer ). All diese Ansätze sind wertvoll.

Sie können dieses Problem auch mit Code in der Mitte des Kontinuums zwischen Kompaktheit und Lesbarkeit lösen. Dieser Ansatz ist fast so lesbar wie die längeren Lösungen, wobei die Länge eher den kleinen, esoterischen Lösungen entspricht.

#!/usr/bin/env bash

read -erp 'Enter numeric grade (q to quit): '
case $REPLY in [qQ]) exit;; esac

declare -A cutoffs
cutoffs[F]=59 cutoffs[D]=69 cutoffs[C]=79 cutoffs[B]=89 cutoffs[A]=100

for letter in F D C B A; do
    ((REPLY <= cutoffs[$letter])) && { echo $letter; exit; }
done
echo "Grade out of range."

In dieser Bash-Lösung habe ich einige leere Zeilen eingefügt, um die Lesbarkeit zu verbessern. Sie können sie jedoch entfernen, wenn Sie es noch kürzer haben möchten.

Leerzeilen eingeschlossen, ist dies eigentlich nur geringfügig kürzer als eine komprimierte, noch gut lesbare Variante der bash-Lösung von AB . Die Hauptvorteile gegenüber dieser Methode sind:

  • Es ist intuitiver.
  • Es ist einfacher, die Grenzen zwischen den Noten zu ändern (oder zusätzliche Noten hinzuzufügen).
  • Eingaben mit führenden und nachfolgenden Leerzeichen werden automatisch akzeptiert (siehe unten für eine Erläuterung der Funktionsweise (( ))).

Alle drei Vorteile ergeben sich aus der Verwendung der Benutzereingabe als numerische Daten und nicht aus der manuellen Prüfung der einzelnen Ziffern.

Wie es funktioniert

  1. Lesen Sie die Eingaben des Benutzers. Lassen Sie sie sich mit den Pfeiltasten im eingegebenen Text bewegen ( -e) und nicht \als Escape-Zeichen interpretieren ( -r).
    Dieses Skript ist keine funktionsreiche Lösung - eine Verfeinerung finden Sie weiter unten - aber diese nützlichen Funktionen verlängern es nur um zwei Zeichen. Ich empfehle, immer mit -rzu verwenden read, es sei denn, Sie wissen, dass Sie dem Benutzer \erlauben müssen, die Eingabe zu unterbrechen.
  2. Wenn der Benutzer qoder geschrieben hat Q, beenden Sie.
  3. Erstellen Sie ein assoziatives Array ( declare -A). Füllen Sie es mit der höchsten numerischen Note aus, die jeder Buchstabennote zugeordnet ist.
  4. Durchlaufen Sie die Buchstabenklassen von der niedrigsten zur höchsten und prüfen Sie, ob die vom Benutzer angegebene Zahl niedrig genug ist, um in den numerischen Bereich der einzelnen Buchstabenklassen zu fallen.
    Bei der (( ))arithmetischen Auswertung müssen Variablennamen nicht mit erweitert werden $. (Wenn Sie in den meisten anderen Situationen den Wert einer Variablen anstelle ihres Namens verwenden möchten, müssen Sie dies tun .)
  5. Wenn es in den Bereich fällt, drucken Sie die Note und beenden Sie das Programm .
    Der Kürze halber verwende ich den Kurzschluss und den Operator ( &&) anstelle eines if- then.
  6. Wenn die Schleife beendet ist und kein Bereich gefunden wurde, gehen Sie davon aus, dass die eingegebene Zahl zu hoch ist (über 100), und teilen Sie dem Benutzer mit, dass sie außerhalb des Bereichs liegt.

Wie sich das verhält, mit seltsamen Eingaben

Wie die anderen kurzen Lösungen geschrieben, wird das Skript nicht die Eingabe zu überprüfen , bevor vorausgesetzt , es ist eine Zahl ist. Arithmetische Auswertung ( (( ))) entfernt automatisch führende und nachfolgende Leerzeichen, das ist also kein Problem, aber:

  • Eingaben, die überhaupt nicht wie eine Zahl aussehen, werden als 0 interpretiert.
  • Bei Eingaben, die wie eine Zahl aussehen (dh wenn sie mit einer Ziffer beginnen), aber ungültige Zeichen enthalten, gibt das Skript Fehler aus.
  • Multi-digit - Eingang mit dem Starten 0wird interpretiert als in oktaler . Das Skript teilt Ihnen beispielsweise mit, dass 77 ein C und 077 ein D ist. Obwohl einige Benutzer dies wünschen, ist dies höchstwahrscheinlich nicht der Fall und kann Verwirrung stiften.
  • Auf der positiven Seite, wenn ein arithmetischer Ausdruck gegeben wird, vereinfacht dieses Skript ihn automatisch und bestimmt die zugehörige Buchstabenklasse. Zum Beispiel wird es Ihnen sagen, 320/4 ist ein B.

Eine erweiterte Vollversion

Aus diesen Gründen möchten Sie möglicherweise so etwas wie dieses erweiterte Skript verwenden, das prüft, ob die Eingabe korrekt ist, und einige andere Verbesserungen enthält.

#!/usr/bin/env bash
shopt -s extglob

declare -A cutoffs
cutoffs[F]=59 cutoffs[D]=69 cutoffs[C]=79 cutoffs[B]=89 cutoffs[A]=100

while read -erp 'Enter numeric grade (q to quit): '; do
    case $REPLY in  # allow leading/trailing spaces, but not octal (e.g. "03") 
        *( )@([1-9]*([0-9])|+(0))*( )) ;;
        *( )[qQ]?([uU][iI][tT])*( )) exit;;
        *) echo "I don't understand that number."; continue;;
    esac

    for letter in F D C B A; do
        ((REPLY <= cutoffs[$letter])) && { echo $letter; continue 2; }
    done
    echo "Grade out of range."
done

Dies ist immer noch eine ziemlich kompakte Lösung.

Welche Funktionen werden hinzugefügt?

Die wichtigsten Punkte dieses erweiterten Skripts sind:

  • Eingabevalidierung. Das Skript von terdon prüft Eingaben mit , daher zeige ich einen anderen Weg, der etwas Kürze einbüßt, aber robuster ist, dem Benutzer die Eingabe von führenden und nachfolgenden Leerzeichen ermöglicht und es ablehnt, einen Ausdruck zuzulassen, der als oktal gedacht sein könnte oder nicht (es sei denn, er ist Null) .if [[ ! $response =~ ^[0-9]*$ ]] ...
  • Ich habe verwendet casemit erweiterten Globbing statt [[mit dem =~ regulären Ausdruck Anpassungsoperator (wie in terdon Antwort ). Ich habe das getan, um zu zeigen, dass (und wie) es auch so gemacht werden kann. Globs und Regexps sind zwei Methoden zum Festlegen von Mustern, die mit Text übereinstimmen. Beide Methoden sind für diese Anwendung geeignet.
  • Wie das Bash-Skript von AB habe ich das Ganze in eine äußere Schleife eingeschlossen (mit Ausnahme der anfänglichen Erstellung des cutoffsArrays). Es fordert Zahlen an und gibt die entsprechenden Buchstabennoten aus, solange eine Terminaleingabe verfügbar ist und der Benutzer nicht angewiesen hat, das Programm zu beenden. Gemessen an den do... doneum den Code in Ihrer Frage, es sieht aus wie Sie das wollen.
  • Um das Beenden zu vereinfachen, akzeptiere ich alle Varianten von qoder , bei denen die Groß- und Kleinschreibung nicht berücksichtigt wird quit.

In diesem Skript werden einige Konstrukte verwendet, die Anfängern möglicherweise unbekannt sind. Sie sind unten aufgeführt.

Erklärung: Verwendung von continue

Wenn ich den Rest des Körpers der äußeren whileSchleife überspringen möchte , verwende ich den continueBefehl. Dies bringt es wieder an den Anfang der Schleife, um weitere Eingaben zu lesen und eine weitere Iteration auszuführen.

Wenn ich das zum ersten Mal mache, ist die einzige Schleife, in der ich mich whilebefinde, die äußere Schleife, sodass ich continueohne Argumente aufrufen kann . (Ich bin in einem caseKonstrukt, aber das hat keinen Einfluss auf den Betrieb von breakoder continue.)

        *) echo "I don't understand that number."; continue;;

Beim zweiten Mal befinde ich mich jedoch in einer inneren forSchleife, die selbst in der äußeren whileSchleife verschachtelt ist . Wenn ich continuekein Argument verwenden würde, wäre dies gleichbedeutend mit continue 1der inneren forSchleife und würde diese anstelle der äußeren whileSchleife fortsetzen.

        ((REPLY <= cutoffs[$letter])) && { echo $letter; continue 2; }

Also verwende ich in diesem Fall, continue 2um bash find zu machen und stattdessen die zweite Schleife fortzusetzen.

Erläuterung: caseBeschriftungen mit Globs

Ich kann nicht caseherausfinden, in welche Briefklasse eine Zahl fällt (wie in ABs bash-Antwort ). Aber ich benutze es case, um zu entscheiden, ob die Eingabe des Benutzers berücksichtigt werden soll:

  • eine gültige Nummer, *( )@([1-9]*([0-9])|+(0))*( )
  • der beendigungsbefehl, *( )[qQ]?([uU][iI][tT])*( )
  • alles andere (und damit ungültige Eingabe), *

Das sind Muschelkugeln .

  • Nach jedem folgt ein ), dem keine Öffnung zugeordnet (ist. Dies ist casedie Syntax zum Trennen eines Musters von den Befehlen, die ausgeführt werden, wenn es zugeordnet ist.
  • ;;caseDies ist die Syntax zum Anzeigen des Endes von Befehlen, die für eine Übereinstimmung mit bestimmten Fällen ausgeführt werden sollen (und dass keine nachfolgenden Fälle getestet werden sollten, nachdem sie ausgeführt wurden).

Durch gewöhnliches Shell-Globbing können *null oder mehr Zeichen, ?genau ein Zeichen und Zeichenklassen / -bereiche in [ ]Klammern abgeglichen werden. Aber ich verwende erweitertes Globbing , das darüber hinaus geht. Erweitertes Globbing ist bei der bashinteraktiven Verwendung standardmäßig aktiviert, bei der Ausführung eines Skripts jedoch standardmäßig deaktiviert. Der shopt -s extglobBefehl oben im Skript aktiviert es.

Erläuterung: Erweitertes Globbing

*( )@([1-9]*([0-9])|+(0))*( ), die nach numerischen Eingaben sucht , entspricht einer Folge von:

  • Null oder mehr Leerzeichen ( *( )). Das *( )Konstrukt stimmt mit keinem oder mehreren Mustern in Klammern überein. Hierbei handelt es sich nur um ein Leerzeichen.
    Tatsächlich gibt es zwei Arten von horizontalen Leerzeichen, Leerzeichen und Tabulatoren, und oft ist es wünschenswert, Tabulatoren auch zuzuordnen. Aber ich mache mir darüber hier keine Sorgen, da dieses Skript für manuelle, interaktive Eingaben und das -eFlag zum readAktivieren von GNU-Readline geschrieben wurde. Auf diese Weise kann der Benutzer in seinem Text mit der linken und rechten Pfeiltaste vor und zurück navigieren. Dies hat jedoch den Nebeneffekt, dass Tabulatoren im Allgemeinen nicht buchstäblich eingegeben werden können.
  • Ein Vorkommen ( @( )) von ( |):
    • Eine von Null verschiedene Ziffer ( [1-9]), gefolgt von Null oder mehr ( *( )) einer beliebigen Ziffer ( [0-9]).
    • Einer oder mehrere ( +( )) von 0.
  • *( )Wieder null oder mehr Leerzeichen ( ).

*( )[qQ]?([uU][iI][tT])*( ), der nach dem Befehl quit sucht , entspricht einer Folge von:

  • Null oder mehr Leerzeichen ( *( )).
  • qoder Q( [qQ]).
  • Optional - dh null oder ein Vorkommen ( ?( )) - von:
    • uoder U( [uU]) gefolgt von ioder I( [iI]) gefolgt von toder T( [tT]).
  • *( )Wieder null oder mehr Leerzeichen ( ).

Variante: Eingabe mit einem erweiterten regulären Ausdruck validieren

Wenn Sie die Benutzereingaben lieber mit einem regulären Ausdruck als mit einem Shell-Glob testen möchten, verwenden Sie möglicherweise lieber diese Version, die dasselbe funktioniert, jedoch [[und =~(wie in der Antwort von terdon ) anstelle von caseund Extended Globbing verwendet.

#!/usr/bin/env bash
shopt -s nocasematch

declare -A cutoffs
cutoffs[F]=59 cutoffs[D]=69 cutoffs[C]=79 cutoffs[B]=89 cutoffs[A]=100

while read -erp 'Enter numeric grade (q to quit): '; do
    # allow leading/trailing spaces, but not octal (e.g., "03")
    if [[ ! $REPLY =~ ^\ *([1-9][0-9]*|0+)\ *$ ]]; then
        [[ $REPLY =~ ^\ *q(uit)?\ *$ ]] && exit
        echo "I don't understand that number."; continue
    fi

    for letter in F D C B A; do
        ((REPLY <= cutoffs[$letter])) && { echo $letter; continue 2; }
    done
    echo "Grade out of range."
done

Mögliche Vorteile dieses Ansatzes sind:

  • In diesem speziellen Fall ist die Syntax zumindest im zweiten Muster, in dem ich nach dem Befehl quit suche, etwas einfacher. Das liegt daran, dass ich die nocasematchShell-Option setzen konnte und dann alle Fallvarianten von qund quitautomatisch abgedeckt wurden.

    Das macht der shopt -s nocasematchBefehl. Der shopt -s extglobBefehl wird weggelassen, da das Globbing in dieser Version nicht verwendet wird.

  • Reguläre Ausdrucksfähigkeiten sind häufiger als das Erlernen von Bash-Extglobs.

Erläuterung: Reguläre Ausdrücke

In Bezug auf die rechts vom =~Operator angegebenen Muster erfahren Sie, wie diese regulären Ausdrücke funktionieren.

^\ *([1-9][0-9]*|0+)\ *$, die nach numerischen Eingaben sucht , entspricht einer Folge von:

  • Der Anfang - dh der linke Rand - der Linie ( ^).
  • Null oder mehr *Leerzeichen ( angewendetes Postfix). Ein Leerzeichen muss normalerweise nicht \in einem regulären Ausdruck -escaped werden, dies wird jedoch benötigt [[, um einen Syntaxfehler zu vermeiden.
  • Eine Teilzeichenfolge ( ( )), die die eine oder andere ( |) von:
    • [1-9][0-9]*: Eine Ziffer ungleich Null ( [1-9]), gefolgt von null oder mehr ( *, angewendetes Postfix) einer beliebigen Ziffer ( [0-9]).
    • 0+: ein oder mehrere ( +, angewendetes Postfix) von 0.
  • Null oder mehr Leerzeichen ( \ *) wie zuvor.
  • Das Ende - dh der rechte Rand - der Linie ( $).

Im Gegensatz zu caseBeschriftungen, die mit dem gesamten getesteten =~Ausdruck übereinstimmen , wird true zurückgegeben, wenn ein Teil des Ausdrucks auf der linken Seite mit dem als Ausdruck auf der rechten Seite angegebenen Muster übereinstimmt. Aus diesem Grund werden die Anker ^und $, die den Anfang und das Ende der Zeile caseangeben , hier benötigt und entsprechen syntaktisch nichts, was in der Methode with und extglobs vorkommt.

Die Klammern werden benötigt, um die Disjunktion von und herzustellen ^und $an diese zu binden . Andernfalls würde dies die Disjunktion von und sein und mit jeder Eingabe übereinstimmen, die mit einer Ziffer ungleich Null beginnt oder mit einer (oder beiden, die möglicherweise noch dazwischen liegende Nicht-Ziffern enthalten) endet .[1-9][0-9]*0+^[1-9][0-9]*0+$0

^\ *q(uit)?\ *$, der nach dem Befehl quit sucht , entspricht einer Folge von:

  • Der Anfang der Zeile ( ^).
  • Null oder mehr Leerzeichen ( \ *siehe obige Erklärung).
  • Der Brief q. Oder Qseit shopt nocasematchist aktiviert.
  • Optional - dh null oder ein Vorkommen (Postfix ?) - der Teilzeichenfolge ( ( )):
    • u, gefolgt von i, gefolgt von t. Oder, da shopt nocasematches aktiviert ist, ukann es sein U; unabhängig sein ikann I; und unabhängig sein tkann T. (Das heißt, die Möglichkeiten sind nicht auf uitund beschränkt UIT.)
  • Wieder null oder mehr Leerzeichen ( \ *).
  • Das Ende der Zeile ( $).
Eliah Kagan
quelle
3
Sag mir die Wahrheit. Wie viel Zeit hat es gedauert? ;)
heemayl
4
@heemayl Ich bin mir nicht ganz sicher, da ich es den ganzen Tag über in vielen kleinen Teilen geschrieben habe (gefolgt von einem vollständigen Durchlesen und Bearbeiten, kurz vor dem Posten). Ich bin mir ziemlich sicher, dass es länger gedauert hat, als ich gedacht hätte, als ich angefangen habe, wenn ich darüber nachgedacht hätte, wie lange es dauern würde. :)
Eliah Kagan
6
schreibe immer mehr, ich brauche ein buch mit deinen antworten.
Grijesh Chauhan
TL; DR hat mich aber trotzdem zum Lachen gebracht!
Fabby
alles vom titel bis zur erklärung ist gut. Ich habe es auf
Anhieb
23

Sie haben bereits die Grundidee. Wenn Sie dies basheinkodieren möchten (was eine vernünftige Wahl ist, da es die Standard-Shell unter Ubuntu und den meisten anderen Linuxen ist), können Sie es nicht verwenden, caseda es keine Bereiche versteht. Stattdessen könnten Sie if/ verwenden else:

#!/usr/bin/env bash

read -p "Please enter your choice: " response

## If the response given did not consist entirely of digits
if [[ ! $response =~ ^[0-9]*$ ]]
then
    ## If it was Quit or quit, exit
    [[ $response =~ [Qq]uit ]] && exit
    ## If it wasn't quit or Quit but wasn't a number either,
    ## print an error message and quit.
    echo "Please enter a number between 0 and 100 or \"quit\" to exit" && exit
fi
## Process the other choices
if [ $response -le 59 ]
then
    echo "F"
elif [ $response -le 69 ]
then
    echo "D"
elif  [ $response -le 79 ]
then
    echo "C"
elif  [ $response -le 89 ]
then
    echo "B"
elif [ $response -le 100 ]
then
    echo "A"
elif [ $response -gt 100 ]
then
    echo "Please enter a number between 0 and 100"
     exit
fi
terdon
quelle
4
-geVermutlich können einige dieser Tests eliminiert werden, da Sie sie verwenden elif. Und keine Liebe für (( $response < X ))?
muru
2
@muru wahr, danke. Ich dachte in Zahlenbereichen fest, aber es gab keinen Grund dazu. Was (( $response < X )), sicher, aber ich finde dies klarer und die OP ist offensichtlich neu zu Bash Scripting.
Terdon
12
#!/bin/bash

while true
do
  read -p "Please enter your choice: " choice

  case "$choice"
   in
      [0-9]|[1-5][0-9])
          echo "F"
          ;;
      6[0-9])
          echo "D"
          ;;
      7[0-9])
          echo "C"
          ;;
      8[0-9])
          echo "B"
          ;;
      9[0-9]|100)
          echo "A"
          ;;
      [Qq])
          exit 0
          ;;
      *) echo "Only numbers between 0..100, q for quit"
          ;;
  esac
done

und eine kompaktere Version (Thx @EliahKagan ):

#!/usr/bin/env bash

while read -erp 'Enter numeric grade (q to quit): '; do
    case $REPLY in
        [0-9]|[1-5][0-9])   echo F ;;
        6[0-9])             echo D ;;
        7[0-9])             echo C ;;
        8[0-9])             echo B ;;
        9[0-9]|100)         echo A ;;

        [Qq])               exit ;;
        *)                  echo 'Only numbers between 0..100, q for quit' ;;
    esac
done
AB
quelle
1
Das sind doch Zeichenbereiche? dh [0-59]bedeutet ein beliebiges Zeichen von 0,1,2,3,4,5 oder 9 und so weiter. Ich verstehe nicht, wie das bei numerischen Werten funktionieren kann .
steeldriver
3
Sie müssen nicht die ganze Zeit FGITW sein. Nehmen Sie sich Zeit, schreiben Sie gute Antworten. Schau dir an, wie Terdon oder Eliah Kagan funktionieren.
muru
@AB Mir ist aufgefallen, dass diese Lösung vor allem durch stilistische Änderungen verkürzt werden kann und dennoch gut lesbar bleibt. Kürze ist selten die wichtigste Überlegung, daher denke ich nicht, dass Sie Ihre Einstellungen auf diese Weise ändern sollten. Da die kompaktere Form jedoch nicht viel Platz einnimmt, können Sie sie auch anzeigen, falls einige Leser ein kürzeres Skript wünschen, das auf die gleiche Weise funktioniert. (Wenn Sie möchten, können Sie gerne meine gekürzte Version oder eine Variation davon verwenden.)
Eliah Kagan
9

Alle Ubuntu-Installationen haben Python. Hier ist also ein Python- Skript für sich . Wenn Sie es in Bash benötigen, habe ich auch das Äquivalent als Shell-Skript geschrieben .

print (chr(75-max(5,int('0'+raw_input('Enter the number: ')[:-1]))))

Um es auszuführen, speichere es in einer Datei (zB grade.py) und starte es dann im Terminal mit diesem Befehl:

python grade.py

Das werden Sie sehen:

Enter the number: 65
E

Wie funktioniert das?

  1. Nehmen Sie die Eingabe - 65.
  2. Fügen Sie am Anfang eine 0 hinzu - 065.
  3. Entfernen Sie das letzte Zeichen 06.
  4. 75 subtrahieren diese Zahl - 70.
  5. In einen Buchstaben umwandeln (A ist 65, B ist 66) - E.
  6. Drucken Sie es aus - E.

Meine Pronomen sind Er / Ihn

Tim
quelle
Ich mag deine Idee. +1
AB
Verwenden Sie nicht input(), es wird anrufen eval(), raw_input()stattdessen verwenden ... auch Ihre Bewertung ist nicht richtig, wie 90+Note Bdrucken wird chr(74 - max(4, num))... verwenden ...
heemayl
well..your Lösung ist schön und wird die in python2 also..just Änderung arbeiten , input()um raw_input()es für python2..thats ..
heemayl
print chr(75-max(5,int('0'+raw_input('Enter the number: ')[:-1])))
heemayl
Dann müssen Sie Ihren ursprünglichen Code too..your Code modifed ändern , wie es derzeit steht , ist falsch wenn wie python3nicht haben raw_input().. Ich schlug vor , raw_input()für Ihre erste ein , wie Sie gesagt haben , es laufen mit python2..
heemayl
6

Hier ist meine semi- esoterische Bash-Lösung, die ein Array mit 101 Einträgen auffüllt und dann Benutzereingaben mit diesen vergleicht. Selbst für den realen Einsatz ist dies vernünftig - wenn Sie eine hervorragende Leistung benötigen, würden Sie Bash nicht verwenden, und hundert (oder so) Aufgaben sind immer noch schnell erledigt. Aber es würde aufhören, vernünftig zu sein, wenn es auf einen viel größeren Bereich ausgedehnt würde (wie eine Million).

#!/usr/bin/env bash
p(){ for i in `seq $2 $3`; do g[$i]=$1; done; }
p A 90 100; p B 80 89; p C 70 79; p D 60 69; p F 0 59
while read -r n && [[ ! $n =~ ^[qQ] ]]; do echo ${g[$n]}; done

Vorteile:

  • Es ist nicht wirklich so esoterisch. Obwohl es länger als die kürzesten Lösungen ist und nicht ganz so selbstdokumentierend wie die längeren Lösungen ... ist es einigermaßen selbstdokumentierend , obwohl es immer noch sehr klein ist.
  • Es ermöglicht eine einfache Änderung, um Notenbereiche zu ändern oder Noten hinzuzufügen / zu entfernen.
  • Er arbeitet in einer Schleife und quitt auf q, quitoder irgendetwas mit Start q/ Q.
  • Listet zuerst die höheren Noten auf, damit Sie positiv denken können. :)
  • Hmm, das macht den Job, macht auch nach dem Anschauen noch Sinn und hat die wesentlichen Funktionen. Sie könnten dies tatsächlich verwenden!

Nachteile:

  • Es gibt dir ein F, wenn du nicht-numerische Eingaben machst ... aber das ist nicht wirklich so schlimm, oder? Wenn Sie eine Nicht-Nummer angeben, für die eine Nummer benötigt wird, verdienen Sie vielleicht ein F!
  • Mehrdeutige, möglicherweise oktale Eingaben werden als oktal behandelt (da ges sich um ein eindimensionales indiziertes Array handelt ). Wie das alte Sprichwort sagt: "Es ist kein Fehler, es ist eine Funktion!" Vielleicht.
  • Bei Eingaben, die außerhalb des Bereichs liegen oder keine Zahl sind, wird eine leere Zeile gedruckt. Daran ist jedoch nichts wirklich Falsches: Es gibt Aufschluss darüber, welcher Buchstabengrad Ihrer Eingabe entspricht, und für falsche Eingaben gibt es keinen.
  • Geben Sie eine negative Zahl ein und nennen Sie es ein Osterei .
  • Immer noch deutlich länger als Tims Python-Lösung . Ja, ich kann das nicht wirklich drehen, um als Vorteil zu erscheinen.

Etwas cool, oder? (Nun, ich denke schon.)

Wie es funktioniert

  1. Die pFunktion p opuliert ein numerisch indiziertes Array gvon g rades in Indizes, die vom ersten bis zum zweiten Argument reichen, wobei der (Buchstaben-) Wert im dritten Argument angegeben wird.
  2. p wird für jede Buchstabenklasse aufgerufen, um ihren Zahlenbereich zu definieren.
  3. Lesen Sie die Benutzereingaben weiter, solange sie verfügbar sind und nicht mit q(oder Q) beginnen, überprüfen Sie das gArray, für das der Buchstabengrad der eingegebenen Zahl entspricht, und drucken Sie diesen Buchstaben aus.
Eliah Kagan
quelle
Was ist mit dieser Bedingung? [[ $n =~ ^(0|[1-9]+[0-9]*)$ ]]
Helio
6

Nachdem ich es in Python 2 gemacht hatte , beschloss ich, es in bash zu machen.

#! /bin/bash

read -p "Enter the number: " i
i=0$i
x=$((10#${i::-1}))
printf "\x$(printf %x $((11-($x>5?$x:5)+64)))\n"

Speichern Sie es zum Ausführen in einer Datei (z. B. grade.sh), machen Sie es mit ausführbar chmod +x grade.shund führen Sie es dann mit aus ./grade.sh.

Das werden Sie sehen:

Enter the number: 65
E

Wie funktioniert das?

  1. Nehmen Sie die Eingabe - 65.
  2. Fügen Sie dem Anfang eine 0 hinzu - 065(und der 10#hält es Basis 10).
  3. Entfernen Sie das letzte Zeichen 06.
  4. 75 subtrahieren diese Zahl - 70.
  5. In einen Buchstaben umwandeln (A ist 65, B ist 66) - E.
  6. Drucken Sie es aus - E.

Meine Pronomen sind Er / Ihn

Tim
quelle
Sehr klug, gut gemacht
kos
@kos danke :) Ich bezweifle, dass es für das OP funktionieren wird, da seine Bereiche wahrscheinlich nicht das sind , was er gepostet hat. Ich gehe davon aus, dass diese der Einfachheit halber sind.
Tim
5

Und hier ist meine awk Version:

awk '{
  if($_ <= 100 && $_ >= 0) {
      sub(/^([0-9]|[1-5][0-9])$/, "F", $_);
      sub(/^(6[0-9])$/, "D", $_);
      sub(/^(7[0-9])$/, "C", $_);
      sub(/^(8[0-9])$/, "B", $_);
      sub(/^(9[0-9]|100)$/, "A", $_);
      print
    }
    else {
      print "Only numbers between 0..100"
    }
}' -

oder als Einzeiler:

awk '{if($_ <= 100 && $_ >= 0) { sub(/^([0-9]|[1-5][0-9])$/, "F", $_); sub(/^(6[0-9])$/, "D", $_); sub(/^(7[0-9])$/, "C", $_); sub(/^(8[0-9])$/, "B", $_);sub(/^(9[0-9]|100)$/, "A", $_);   print} else { print "Only numbers between 0..100"}}' -
AB
quelle
4

Hier ist eine weitere "esoterische" Antwort

perl -E '
    print "number: "; 
    $n = <>; 
    say qw/A A B C D E F F F F F/[11-($n+1)/10]
       if $n=~/^\s*\d/ and 0<=$n and $n<=100
'

Erläuterung

  • perl -E: Die -E, wie -eermöglicht ein Skript als Kommandozeilen - Argument übergeben. Dies ist eine Möglichkeit, Perl-Einzeiler auszuführen. Im Gegensatz dazu -ewerden -Eauch alle optionalen Funktionen aktiviert (wie zum Beispiel say, die im Grunde genommen printmit einem nachgestellten Zeilenumbruch versehen sind.).
  • print "number: "; : Fordert den Benutzer auf, eine Nummer einzugeben.
  • $n = <>;: Speichern Sie diese Nummer als $n.

Das nächste Stück muss noch etwas abgebaut werden. qw/string/Wertet eine Liste aus, die durch Brechen stringmit Leerzeichen erstellt wurde. Also, qw/A A B C D E F F F F F/ist eigentlich diese Liste:

0 : A
1 : A
2 : B
3 : C
4 : D
5 : E
6 : F
7 : F
8 : F
9 : F
10 : F

Daher say qw/A A B C D E F F F F F/[11-($n+1)/10]ist gleichbedeutend mit

my @F=("A","A","B","C","D","E","F","F","F","F","F");
print "$F[11-($n+1)/10]\n"

In Perl können jetzt negative Indizes verwendet werden, um Elemente vom Ende des Arrays abzurufen. Zum Beispiel $arrray[-1]wird das letzte Element des Arrays gedruckt. Darüber hinaus werden Gleitkomma-Array-Indizes (z. B. 10.7) automatisch auf die nächstniedrigere Ganzzahl (10.7 oder 10.3 oder was auch immer alles zu 10 wird) gekürzt.

Das Ergebnis all dessen ist, dass der Index 11-($n+1)/10immer das entsprechende Element (Grad) des Arrays ergibt .

Glenn Jackman
quelle
4
Esoterische Antworten sind alle nett, aber bitte fügen Sie eine Erklärung bei.
muru
1

Obwohl Sie nach einer Bash-Lösung gefragt haben, denke ich in Python, kann dies auf elegante und kurze Weise geschehen. Behandeln Sie sowohl Fehler bei der Handhabung bei falscher Eingabe als auch die "Umwandlung" einer Zahl zwischen 0 und 100 in Buchstaben von A nach F (oder eine andere):

#!/usr/bin/env python3
try:
    n = int(input("number: ")); n = n if n>0 else ""
    print("FEDCBA"[[n>=f for f in [50,60,70,80,90,101]].count(True)])
except:
    print("invalid input")

Erläuterung

  1. Zuerst müssen wir die Nummer vom Benutzer bekommen:

    n = int(input("number: "))
  2. Wir testen diese Nummer auf Gültigkeit für eine Reihe von Bedingungen:

    n>=50, n>=60, n>=70, n>=80, n>=90

    Für jeden dieser Tests ist das Ergebnis entweder Falseoder True. Deshalb (den Code etwas komprimieren):

    [n>=f for f in [50,60,70,80,90]].count(True)]

    wird eine Figur von 0bis produzieren5

  3. Anschließend können wir diese Zahl als Index für einen String verwenden, um ein Zeichen als Ausgabe zu erzeugen, z

    "ABCDEF"[3] 

    gibt "D" aus (seit dem ersten Zeichen = "A")

  4. Der Zusatz 101zur Liste besteht darin, einen (Index-) Fehler zu erzeugen, falls die Anzahl überschritten wird 100, da dieser "ABCDEF"[6]nicht existiert. Gleiches gilt für n = n if n>=0 else "": Wenn eine Zahl unter 0 eingegeben wird, tritt ein (Wert-) Fehler auf.
    In diesen Fällen und wenn die Eingabe keine Zahl ist, lautet das Ergebnis:

    invalid input

Die Tests:

number: 10
F

number: 50
E

number: 60
D

number: 70
C

number: 80
B

number: 90
A

number: 110
invalid input

number: -10
invalid input

number: Monkey
invalid input
Jacob Vlijm
quelle