Eigene Befehle

Die Fähigkeit von Nu, lange Pipelines zu verarbeiten, erlauben es große Kontrolle über Daten und das System zu haben. Das Ganze kommt allerdings zum Preis von viel Tipparbeit. Idealerweise sollte es eine Möglichkeit geben, mühsam gebaute Pipelines zu speichern und wieder und wieder auszuführen.

Hier kommen eigene Befehle ins Spiel.

Eine beispielhafte Definition eines eigenen Befehls sieht wie folgt aus:

def greet [name] {
  echo "hello" $name
}

In dieser Definition, wird ein Befehl greet beschrieben, der einen Parameter name konsumiert. Nach diesem Parameter erfolgt die Beschreibung was passiert, wenn der Befehl ausgeführt wird. Wenn der Befehl aufgerufen wird, wird der Wert, der als Parameter name übergeben wurde, in die Variable $name geschrieben, die im Codeblock verfügbar ist.

Um den obigen Befehl auszuführen wird er wie ein eingebauter Befehl aufgerufen:

> greet "world"

Wenn das getan wird, wird eine Ausgabe erzeugt, die wie die der eingebauten Befehle aussieht:

───┬───────
 0 │ hello
 1 │ world
───┴───────

Namen von Befehlen

In Nushell ist ein valider Name eines Befehls ein String aus Zeichen oder ein String in Anführungszeichen. Beispiele hierfür sind: greet, get-size, mycommand123, "mycommand", 😊 und 123.

Hinweis: Es wird empfohlen Worte in Befehlen mit - zur besseren Lesbarkeit zu trennen. Beispiele: get-size anstatt getsize oder get_size.

Unterbefehle

Es ist auch möglich Unterbefehle zu definieren. Dazu wird der Unterbefehl vom Superbefehl durch ein Leerzeichen getrennt. Wenn beispielsweise der Befehl str durch einen Unterbefehl mycommand erweitert werden soll, funktioniert das wie folgt:

def "str mycommand" [] {
  echo hello
}

Jetzt kann der eigene Unterbefehl aufgerufen werden, als ob er ein eingebauter Befehl von str wäre:

> str mycommand

Typen von Parametern

Wenn eigene Befehle definiert werden, kann optional auch der Typ jedes Parameters angegeben werden. Das obige Beispiel kann beispielsweise wie folgt abgeändert werden:

def greet [name: string] {
  echo "hello" $name
}

Die Typen der Parameter anzugeben ist optional. Nushell erlaubt es diese wegzulassen und behandelt diese dann als Typ any. Es kann also jede Art von Typ verarbeitet werden. Wenn ein Typ angegeben wurde, überprüft Nushell den Typ, wenn die Funktion aufgerufen wird.

Beispielhaft soll nur noch ein int als Typ erlaubt sein:

def greet [name: int] {
  echo "hello" $name
}

greet world

Wenn versucht wird, den oberen Code auszuführen, wird Nu darauf aufmerksam machen, dass die Typen nicht passen und die Ausführung stoppen:

error: Type Error
  ┌─ shell:6:7
  │
5 │ greet world
  │       ^^^^^ Expected int, found world

Dies kann dabei helfen Nutzer darauf aufmerksam zu machen, welche Art von Typ erlaubt ist.

Die aktuell erlaubten Typen sind (mit Version 0.65.0 und neuer):

  • any
  • block
  • cell-path
  • duration
  • path
  • expr
  • filesize
  • glob
  • int
  • math
  • number
  • operator
  • range
  • cond
  • bool
  • signature
  • string
  • variable
  • record
  • list
  • table
  • error

Flags

Zusätzlich zu den obigen Parametern, können auch namenabhängige Parameter verwendet werden, indem Flags für eigene Befehle definiert werden.

Zum Beispiel:

def greet [
  name: string
  --age: int
] {
  echo $name $age
}

In der obigen Definition von greet, werden ein fester Parameter name und eine Flag age definiert. Damit ist es möglich, dem Befehl greet optional den Parameter age zu übergeben.

Das obige Beispiel kann wie folgt aufgerufen werden:

> greet world --age 10

Oder:

> greet --age 10 world

Oder gleich ganz ohne Flag:

> greet world

Flags können auch so definiert werden, dass es eine Kurzform gibt. Das erlaubt es sowohl eine kurze als auch eine einfach lesbare lange Flag für die selbe Aufgabe zu haben.

Das Beispiel wird hier, um eine Kurzform für die Flag age erweitert:

def greet [
  name: string
  --age (-a): int
] {
  echo $name $age
}

Hinweis: Flags sind benannt nach der langen Form des Namens. Im obigen Beispiel erfolgt der Zugriff immer über $age und nicht über $a.

Nun kann diese neue Version von greet wie folgt aufgerufen werden:

> greet -a 10 hello

Dokumentation für den eigenen Befehl

Um Nutzern eines eigenen Befehls zu helfen, können diese und ihre Parameter mit zusätzlichen Beschreibungen versehen werden.

Es wird weiterhin das obige Beispiel verwendet:

def greet [
  name: string
  --age (-a): int
] {
  echo $name $age
}

Wenn der Befehl definiert ist kann help greet aufgerufen werden, um Informationen zum Befehl zu erhalten:

Usage:
  > greet <name> {flags}

Parameters:
  <name>

Flags:
  -h, --help: Display this help message
  -a, --age <integer>

Wie zu sehen ist, werden der Parameter und die Flag, die definiert wurden, aufgelistet. Zusätzlich gibt es noch die Flag -h, die jeder Befehl hat.

Um diese Hilfe zu verbessern, können Beschreibungen zur Definition hinzugefügt werden:

# A greeting command that can greet the caller
def greet [
  name: string      # The name of the person to greet
  --age (-a): int   # The age of the person
] {
  echo $name $age
}

Diese Kommentare, die zur Definition und den Parametern hinzugefügt wurden, werden sichtbar, wenn die Hilfe zum Befehl aufgerufen wird.

Wenn jetzt help greet ausgeführt wird, wird ein hilfreicherer Text angezeigt:

A greeting command that can greet the caller

Usage:
  > greet <name> {flags}

Parameters:
  <name> The name of the person to greet

Flags:
  -h, --help: Display this help message
  -a, --age <integer>: The age of the person

Ausgabe

Eigene Befehle streamen ihre Ausgabe gleich wie eingebaute Befehle. Beispielsweise soll die folgende Pipeline umgebaut werden:

> ls | get name

ls soll jetzt in einen neuen, eigenen Befehl verschoben werden:

def my-ls [] { ls }

Die Ausgabe dieses Befehls, kann identisch zur Ausgabe von ls verwendet werden.

> my-ls | get name
───┬───────────────────────
 0 │ myscript.nu
 1 │ myscript2.nu
 2 │ welcome_to_nushell.md
───┴───────────────────────

Das erlaubt es sehr einfach eigene Befehle zu definieren und deren Ausgabe zu verwenden. Ein Hinweis: Es werden keine return Statements wie in anderen Sprachen verwendet. Stattdessen werden in Nushell Pipelines gebaut, die ihre Ausgabe zur verbundenen Pipeline streamen.

Eingabe

Eigene Befehle können, wie andere Befehle, auch Eingaben verarbeiten. Diese Eingabe wird durch die Pipeline an den Codeblock des eigenen Befehls übergeben.

Hier soll nun beispielhaft ein eigener echo-Befehl definiert werden, der eine weitere Zeile nach jeder Zeile der Eingabe ausgibt:

def my-echo [] {
  each {
    echo $it "--"
  }
}

Wenn dieser neue Befehl nun in einer Pipeline aufgerufen wird, sieht die Ausgabe wie folgt aus:

> echo foo bar | my-echo
───┬─────
 0 │ foo
 1 │ --
 2 │ bar
 3 │ --
───┴─────

Persistenz

Um Informationen darüber zu erhalten, wie eigene Befehle bei jedem Start von Nushell verfügbar bleiben, sei auf das Konfigurationskapitel verwiesen.