it-swarm.com.de

Wie kann ich lesen und wiedergeben, was immer der Benutzer eingegeben hat?

Ich mache dieses Skript: wenn Sie tippen

"my name is [your name]"

es sagt dir

"hi, [your name]".

Ich weiß jedoch nicht, wie ich [your name] zu etwas machen soll, das Sie eingeben. Ich habe ein Skript mit einem bestimmten Namen erstellt, möchte jedoch, dass der Name, den der Benutzer eingibt, wiedergegeben wird:

#!/bin/bash
read -p "Say something: " sth
  if [[ $sth = "my name is ralph" ]]
      then
          echo "Hi $(echo $sth | cut -f 4 -d ' ')"
  else
      echo "I didn't understand that"
  fi

Das wird also Hi ralph wiedergeben, aber wie soll es Hi [your name] mit dem Namen wiedergeben, den Sie eingegeben haben?

1
user661429

Die Einzelheiten werden nicht von Ihnen angegeben, aber im Allgemeinen erfassen Sie Name so.

#!/bin/bash   

regex='[Mm]y(\ name\ is\ )(\w*)'

read -p "Say something: " response

echo $response

if [[ "$response" =~ $regex ]]; then

        name=$(echo $response | cut -d' ' -f4)

        echo "Hi $name"
else
        echo "I didn't understand that"

fi
3
George Udosen

Sie können den Regex-Test =~ verwenden, um zu erfassen, was nach my name is kommt:

$ read -rp "Say something: "; if [[ "$REPLY" =~ [Mm]y\ name\ is\ .* ]]; then echo "Hi "${REPLY:11}"" ; fi
Say something: my name is zanna
Hi zanna

Hier habe ich ein Parametererweiterung verwendet, um die ersten 11 Zeichen (my name is) zu entfernen und das, was danach kam, auszudrucken. Wenn der Benutzer jedoch mehr als seinen Namen eingibt, ist das Ergebnis möglicherweise nicht das, was Sie wollen:

Say something: my name is pixie and I eat flowers
Hi pixie and I eat flowers

Georges Antwort behandelt dies, indem cut verwendet wird, um nur das vierte Feld zu drucken (aber ich denke, der Benutzer gibt möglicherweise My name is Super Rainbow Unicorn ein, und Sie möchten möglicherweise nicht, dass die Shell nur Hi Super antwortet).

lesbarer und mit einem else:

read -rp "Say something: "
if [[ "$REPLY" =~ [Mm]y\ name\ is\ .* ]]
  then 
    echo "Hi "${REPLY:11}""
  else
    echo "I didn't understand that."
fi
3
Zanna

Zanna und George haben beide das Richtige getan, um reguläre Ausdrücke zu verwenden, haben dann aber aufgehört, Bashs reguläre Ausdrücke zu verwenden, um den Namen zu extrahieren. Mit etwas wie:

regex='[Mm]y(\ name\ is\ )(\w*)'
read -p "Say something: " response

if [[ "$response" =~ $regex ]]; then

Nachdem der Regex-Test [[ ]] abgeschlossen ist, stellt bash die im Array BASH_REMATCH übereinstimmenden Regex-Gruppen zur Verfügung. Zum Beispiel (mit Eingabe My name is foo bar baz):

$ printf "%s\n" "${BASH_REMATCH[@]}"
My name is foo
 name is 
foo

So ändern Sie die Gruppen ein wenig:

$ regex='My name is (\w.*)'
$ [[ "$response" =~ $regex ]]
$ printf "%s\n" "${BASH_REMATCH[@]}"
My name is foo bar baz
foo bar baz

Besser noch, Sie können bash anweisen, den Regex-Abgleich ohne Berücksichtigung der Groß- und Kleinschreibung zu verwenden:

shopt -s nocasematch

All dies kombinieren:

regex='My name is (\w.*)'
read -rp "Say something: "
shopt -s nocasematch
if [[ $REPLY =~ $regex ]]
then 
    echo "Hi ${BASH_REMATCH[1]}"
else
    echo "I didn't understand that."
fi

(Dies eignet sich auch sehr gut zum Extrahieren des ersten oder des letzten Wortes eines Namens.)

2
Olorin

Zwei Codezeilen: printf für die Eingabeaufforderung, sed für alles andere.

Sie könnten erwägen, read überhaupt nicht zu verwenden. Es gibt viele Standarddienstprogramme, die alleine oder in Skripten verwendet werden können. Ein Dienstprogramm, das dafür funktioniert, ist sed , der Stream-Editor .

printf 'Say something: '
sed -r 's/^[Mm]y name is (.+)/hi, \1/; tQ; s/.*/I didn\x27t understand that/; :Q; q'

Das ist es. Das ist alles was du brauchst.

Wie es funktioniert

printf druckt die Eingabeaufforderung. Es wird kein Zeilenumbruch angehängt.

sed verarbeitet die Eingabe zeilenweise. Wenn kein Dateiname angegeben wird, wird von Standardeingabe gelesen, ebenso wie vom Befehl read. In diesem Fall stoppen wir nach nur der ersten Zeile (wie es der Befehl read tut), was das q am Ende ist - es wird beendet. Im Allgemeinen wird sed verwendet, um mehrere Eingabezeilen zu verarbeiten, häufig jede Zeile in einer Datei. Hier möchten wir jedoch nur eine Zeile.

Sowohl Bash als auch Sed sind Sprachen und beide haben eine Vorstellung von Befehlen . Jede Zeile darüber ist ein einzelner Bash-Befehl, aber innerhalb des Skripts mit einfachen Anführungszeichen Sed, das an sed übergeben wird, gibt es mehrere Sed - Befehle.

Der erste Sed Befehl ist s/[Mm]y name is (.+)/hi, \1/. Es führt Substitution durch (s/). Es sucht:

  • ^ - um ganz am Anfang der Zeile
  • [Mm] - für M ODER m
  • y name is - für diesen wörtlichen Text, einschließlich des nachgestellten Leerzeichens
  • (.+) - für ein oder mehrere (+) von ein beliebiges Zeichen (.). Dies ist der Name des Benutzers. Da es in Klammern steht und dies das erste Mal ist, dass ( im Muster vorkommt, wird es in der ersten Gruppe erfasst und kann über die erste Rückverweis , \1 aufgerufen werden.

Es ersetzt solchen Text durch:

  • hi, - dieser wörtliche Text, einschließlich des nachgestellten Leerzeichens
  • \1 - Der Text, der mit .+ im Suchmuster übereinstimmt

Der zweite Sed Befehl ist tQ. Sie können diesen t Q auch schreiben. It tests :

  • wenn die Übereinstimmungsoperation, die mit dem vorherigen Befehl s versucht wurde, erfolgreich war.
  • In diesem Fall wird zum später angezeigten label :Q gesprungen.

Damit wird das Ziel erreicht, den nächsten Befehl zu überspringen, es sei denn, der Benutzer hat keine verwendbaren Eingaben gemacht. Die Funktion des nächsten Befehls besteht darin, den Benutzer zu informieren, dass seine Eingabe schließlich nicht verstanden wurde.

Der dritte Sed Befehl ist s/.*/I didn\x27t understand that/. Es sucht nach:

  • .* - Null oder mehr Zeichen. Dies entspricht immer der gesamten Zeile.

Dies bietet die Möglichkeit, mit der Eingabe nichts weiter zu tun und stattdessen alles durch die Nachricht zu ersetzen, die wir anzeigen möchten:

  • I didn - dieser wörtliche Text
  • \x27 - ein '-Zeichen, da wir ' in der Shell verwenden, um das gesamte Skript an sed zu übergeben (andernfalls wäre es in Ordnung, ein wörtliches ' in das Sed - Skript aufzunehmen)
  • t understand that - dieser wörtliche Text

Das Label :Q. Labels in Sed beginnen mit einem :, aber wenn sie mit dem Befehl Sed zu t (siehe oben) verzweigt werden, wird der : weggelassen. Sie können dies so nennen, wie Sie möchten. Ändern Sie einfach alle t-Befehle, die es entsprechend verwenden.

Der letzte Sed Befehl , q. Dies beendet ​​sed. Da dieser Befehl Sed immer nach der Verarbeitung einer Zeile auftritt, wird nie mehr als eine Zeile verarbeitet.

Überlegungen zur Portabilität

Der oben gezeigte sed-Befehl ist tatsächlich nicht für alle Sed -Implementierungen übertragbar, da er zwei nicht standardmäßige Funktionen verwendet, sondern speziell von GNU sed bereitgestellt wird, dem Sed Implementierung in den meisten GNU/Linux-Systemen einschließlich Ubuntu.

  1. Die Escape-Sequenz \x27 bedeutet ' (und hexadezimales Escape im Allgemeinen).
  2. Semikolons um Bezeichnungen. Der ; ist im Allgemeinen genauso gut wie eine neue Zeile, um separate Sed - Befehle aufzuteilen, aber einige Implementierungen erlauben es nicht, Beschriftungen zu verwenden.

Wenn Sie diese Probleme tatsächlich beheben möchten, können Sie den gleichen Befehl auch auf anderen Betriebssystemen wie macOS und FreeBSD ausführen, die nicht über GNU Sed verfügen (es sei denn, Sie installieren ihn auf ihnen) ), dann können Sie einfach verwenden:

sed -r 's/^[Mm]y name is (.+)/hi, \1/
tQ
s/.*/I didn'\''t understand that/
:Q
q'

Dies funktioniert, weil Bourne-artige Shells wie Bash das Anzeigen von wörtlichen Zeilenumbrüchen in einfachen Anführungszeichen ermöglichen. Die Sequenz '\'' endet mit dem Zitieren, liefert einen ', der aufgrund des unmittelbar vorhergehenden \-Zeichens selbst separat zitiert wird, und setzt dann single fort Zitat. Dies setzt immer noch voraus, dass Sie eine Shell im Bourne-Stil verwenden, jedoch nicht unbedingt bash.

Eine Alternative ist die Verwendung der $''-Syntax, die eigentlich nicht standardisiert ist und die nicht von allen Bourne-Shells unterstützt wird, obwohl dies von mehreren gängigen unterstützt wird. Auf diese Weise können Sie es wahrscheinlich immer noch als echten Einzeiler schreiben, während Sie nur die Standardfunktionen Sed verwenden, obwohl ich Sie auffordern , dies nicht zu tun auf diese Weise, weil es verwirrender ist:

sed -r $'s/^[Mm]y name is (.+)/hi, \\1/\ntQ\n s/.*/I didn\x27t understand that/\n:Q\nq'

Abgesehen davon, dass \x27 durch ' ersetzt wird, wird \\ durch \ und \n durch eine neue Zeile ersetzt, sodass sed dasselbe Sed Skript erhält wie mit dem obigen mehrzeiligen Befehl, den Sie ohnehin verwenden sollten.

Mein Dank geht an Zanna , der vorgeschlagenes t verwendet werden könnte und hat mir geholfen zu vereinfachen = das Skript Sed, sobald es geschrieben wurde.

2
Eliah Kagan