`gosh` eine Shell, in golang geschrieben. ``` gosh - Go Shell Version: 0.13.0 (branch: main, commit: f79fb0b) Built-in commands: cd [dir] - Change directory (cd with no args goes to home) pwd - Print working directory echo [args] - Echo arguments exit [code] - Exit shell with status code (default: $?) quit [code] - Quit shell with status code (default: $?) set VAR=val - Set environment variable set -- args - Set the positional parameters $1, $2, ... export VAR=val - Export environment variable unset VAR - Remove a variable (unset -f name: a function) source file [args] - Run the commands of file in this shell (also: . file) jobs - List background and stopped jobs fg [%n] - Continue job n (default: current job) in the foreground bg [%n] - Continue stopped job n in the background wait [%n] - Wait for job n or all background jobs alias [name[=value] ...] - Define or show aliases (alias ll='ls -l') unalias name - Remove an alias (unalias -a: all aliases) : - Do nothing (exit status 0) break [n] - Leave the innermost (or n-th) for/while/until loop continue [n] - Continue with the next iteration of a loop return [n] - Leave a function or sourced file with status n local VAR[=val] - Variable that gets its old value back when the function returns shift [n] - Remove the first n positional parameters (default 1) read [-r] [-p prompt] [VAR ...] - Read a line into variables (split at $IFS; the last one gets the rest; default $REPLY) help - Show this help Features: - Argument parsing with quotes and escapes ('...' is literal, "..." expands variables, \ escapes the next character) - Ctrl+D to exit, Ctrl+C to cancel the current line or command - Command history (up/down arrows), stored in $XDG_STATE_HOME/gosh/history (default ~/.local/state/gosh/history, override with $GOSH_HISTFILE); lines starting with a space are not saved - Tab completion for commands and file paths - Tilde expansion (~, ~/path) at the start of unquoted words - Brace expansion: a{b,c}d -> abd acd, {1..5}, {01..10..2}, {a..e} - Wildcards: * ? [abc] [a-z] [!abc] match file names (not hidden files unless the pattern starts with a dot); quoted wildcards and values of variables are not expanded, a pattern without match is kept as is - Prompt user@host:dir $, with [N] before the $ if the last command failed with exit status N - Colored prompt (user@host green, red for root; directory blue; exit status red), configurable with $GOSH_COLORS, e.g. GOSH_COLORS='user=1;33:path=36' (keys user, root, path, error; values are ANSI SGR codes, empty = no color); disabled with $NO_COLOR, TERM=dumb or when output is not a terminal - Variables: name=value sets a variable (like set), name=value command sets it only for that command; the value is not split or expanded as a pattern - Environment variable expansion ($VAR, ${VAR}, ${VAR:-default}); using an unset variable is an error, use ${VAR:-} for an empty default - Positional parameters: $0, $1 ... $9, ${10}, $# (count), "$@" (one word each), "$*" (one word), $$ (process ID); set by gosh script args, gosh -c command name args, functions, set -- and source - Command substitution: $(command) is replaced by the output of command (run in a subshell); unquoted it is split into words, "$(command)" stays one word - Command lists: a ; b (run both), a && b (b only if a succeeded), a || b (b only if a failed); $? is the exit status of the last command - Control structures: if cmd; then ...; elif cmd; then ...; else ...; fi, for x in words; do ...; done, while cmd; do ...; done, until ..., case word in pattern|pattern) ...;; esac, { cmd; cmd; } groups, ( cmd; cmd ) subshells; ! cmd negates the status; they can span several lines (prompt "> "), have redirections (done < file) and can be used in pipes (| while read l; do ...; done) and with & - Functions: name() { ...; } (or function name { ...; }); the arguments are $1, $2, ...; in pipes, & and $(...) they run in a subshell - Pipes (a | b) and redirections: < file, > file, >> file, 2> file, 2>> file, 2>&1, >&2, &> file (stdout and stderr), &>> file, </dev/null; do sleep 1; done case "$1" in start|run) echo "starte" ;; stop) echo "stoppe" ;; *.tar.gz) tar xzf "$1" ;; *) echo "unbekannt: $1" ;; esac test -d build || { echo "kein build-Verzeichnis"; exit 1; } ``` | Konstrukt | Bedeutung | |---------------------------------------------|-------------------------------------------------------------------| | `if befehl; then …; elif …; then …; else …; fi` | führt den Zweig aus, dessen Bedingung den Exit-Status 0 hat | | `for x in wörter; do …; done` | setzt `x` nacheinander auf jedes Wort | | `while befehl; do …; done` | wiederholt, solange `befehl` erfolgreich ist | | `until befehl; do …; done` | wiederholt, bis `befehl` erfolgreich ist | | `case wort in muster\|muster) …;; esac` | führt den ersten Zweig aus, dessen Muster auf `wort` passt | | `{ befehl; befehl; }` | fasst Befehle zusammen (z. B. nach `\|\|`) | | `( befehl; befehl )` | führt die Befehle in einer Subshell aus | | `! befehl` | kehrt den Exit-Status um | | `break [n]`, `continue [n]` | verlässt die (n-te) Schleife bzw. springt zum nächsten Durchlauf | | `:` | tut nichts, Exit-Status 0 (z. B. `while :; do …; done`) | - Bedingungen sind normale Befehle (auch Listen mit `&&`/`||`), meist `test` bzw. `[ … ]` (z. B. `[ -f datei ]`, `[ "$a" = b ]`, `[ $n -lt 3 ]`). - Die Wörter nach `for … in` werden wie Argumente expandiert: `*.go`, `{1..10}`, `$VAR`, `$(befehl)`, `"$@"`. Ohne `in` läuft die Schleife über die Positionsparameter (`for a; do …` ist `for a in "$@"; do …`). - `case`: Die Muster sind Wildcards wie bei Dateinamen (`*`, `?`, `[…]`), nur passen `*` und `?` hier auch auf `/`. Mehrere Muster werden mit `|` getrennt, das `(` vor dem Muster ist optional, das `;;` im letzten Zweig auch. Zeichen in Anführungszeichen passen nur wörtlich (`"*")` passt nur auf `*`). Wie überall in gosh sind Werte von Variablen keine Muster: Nach `p='*.go'` passt `$p)` nur auf den Text `*.go`. Passt kein Muster, ist der Status 0. - `{ …; }`: `{` und `}` sind nur am Anfang eines Befehls Schlüsselwörter, vor dem `}` muss deshalb ein `;` oder ein Zeilenumbruch stehen. Die Befehle laufen in der Shell selbst (keine Subshell). - Variablen, `$(...)` und Wildcards im Rumpf werden bei jedem Durchlauf neu ersetzt. - Ist ein Befehl noch nicht vollständig (z. B. nach `if …; then` oder `for …` + Enter, nach `&&`, `|` oder einem `\` am Zeilenende), zeigt gosh den Fortsetzungs-Prompt `> `; Ctrl+C verwirft die angefangene Eingabe. Skripte und Startdateien lesen entsprechend weiter. - Ctrl+C bricht die ganze Befehlszeile ab, also auch eine Schleife (`while true; do sleep 1; done`). - Der Exit-Status ist der des zuletzt ausgeführten Befehls; ein `if` ohne passenden Zweig und eine Schleife ohne Durchlauf haben den Status 0. - Arithmetik geht noch nicht mit `$((…))`, sondern z. B. mit `n=$(expr $n + 1)`. ### Umleitungen, Pipes und Hintergrund bei Kontrollstrukturen Kontrollstrukturen lassen sich wie einfache Befehle umleiten, in Pipes verwenden und im Hintergrund starten: ```bash # Datei Zeile für Zeile lesen while read -r name rest; do echo "Name: $name" done < /etc/passwd # Ausgabe einer Schleife sortieren bzw. umleiten for f in *.log; do wc -l < "$f"; done | sort -n if make; then echo ok; else echo kaputt; fi > build.status 2>&1 { date; uptime; } >> status.log # Zeilen aus einer Pipe verarbeiten git ls-files | while read -r f; do echo "== $f"; done # Schleife im Hintergrund for host in a b c; do ping -c1 $host; done > ping.log & ``` - Umleitungen nach `done`, `fi`, `esac`, `}` und `)` gelten für alle Befehle darin. Die Kontrollstruktur läuft dabei in der Shell selbst, Variablen aus `while read x; do …; done < datei` sind danach also gesetzt. - In einer Pipe und im Hintergrund (`&`) läuft eine Kontrollstruktur (wie in `sh`) in einer Subshell, einem eigenen gosh-Prozess, weil alle Teile einer Pipe gleichzeitig laufen. Variablen, die dort gesetzt werden, gelten danach nicht mehr: Nach `ls | while read f; do n=$f; done` ist `n` nicht gesetzt. Die Subshell kennt die Funktionen und Positionsparameter der Shell. - Auch Listen lassen sich in den Hintergrund schicken: `make && make install &` läuft als ein Job in einer Subshell. - Mit Ctrl+C wird die ganze Pipe abgebrochen, auch eine Endlosschleife wie `while true; do date; sleep 1; done | tee log`. ### Subshells `( befehle )` führt Befehle in einer Subshell aus, einem eigenen gosh-Prozess. Änderungen an Verzeichnis, Variablen und Funktionen gelten nur dort: ```bash (cd /tmp && tar xzf ~/paket.tar.gz) # danach ist man noch im alten Verzeichnis (export LANG=C; sort datei) (exit 3); echo $? # 3 (cd build && make) > build.log 2>&1 ``` - `exit` beendet nur die Subshell, ihr Exit-Status ist der des letzten Befehls bzw. von `exit`. - Die Subshell bekommt alle Variablen (in gosh sind alle exportiert), die Funktionen und die Positionsparameter. Aliase gelten dort nicht (wie in Skripten). - `{ …; }` fasst dagegen Befehle zusammen, ohne eine Subshell zu starten. - `(` und `)` sind Operatoren: Ein Wort wie `a(b)` ist ein Syntaxfehler, Klammern als Text brauchen Anführungszeichen oder `\` (`echo "(a)"`, `find . \( -name x \)`). ### Funktionen ```bash # Definition (auch: function gruss { … }) gruss() { local name=${1:-Welt} # local: nur in der Funktion echo "Hallo $name" } gruss # Hallo Welt gruss Flo # Hallo Flo gruss Flo > datei # Umleitungen gelten für alle Befehle der Funktion gruss Flo | tr a-z A-Z # auch in Pipes x=$(gruss Flo) ist_root() { [ "$(id -u)" = 0 ] && return 0 return 1 } ist_root || echo "kein root" # Die Funktion liest von stdin zeilen() { while read -r zeile; do echo "> $zeile"; done } zeilen < /etc/hostname ls | zeilen ``` - Eine Funktion wird mit `name() { …; }`, `name () { …; }` oder `function name { …; }` definiert. Der Rumpf ist ein zusammengesetzter Befehl, meist `{ …; }`, aber auch `if`, `for`, `while` oder `case` sind möglich. Die Definition darf über mehrere Zeilen gehen. - Die Argumente des Aufrufs sind in der Funktion `$1`, `$2`, …, `$#` und `"$@"`; danach haben die Positionsparameter wieder ihre alten Werte. `$0` bleibt der Name des Skripts. - `return [n]` beendet die Funktion mit dem Status `n` (ohne `n`: der Status des letzten Befehls). Ohne `return` ist der Status der des letzten Befehls. - Funktionen laufen in der Shell selbst: Variablen, die sie setzen, und `cd` gelten danach weiter. `local name[=wert]` macht eine Variable lokal, sie bekommt beim Verlassen der Funktion ihren alten Wert zurück (Funktionen, die von dort aufgerufen werden, sehen den lokalen Wert). - `name=wert funktion` setzt die Variable nur während des Aufrufs. - In einer Pipeline, im Hintergrund (`funktion &`) und in `$(...)` läuft eine Funktion in einer Subshell, Änderungen an Variablen gehen dort also verloren (wie in `sh`). Die Subshell kennt alle Funktionen. - Eine Funktion hat Vorrang vor Built-ins und Programmen gleichen Namens. Weil es noch kein `command` gibt, kann eine Funktion `ls` das Programm `ls` aber nicht aufrufen. Mit `\ls` umgeht man nur Aliase. Besser ist ein anderer Name, z. B. `ll() { ls -l "$@"; }`. - `unset -f name` entfernt eine Funktion. Rekursion ist erlaubt, aber nur bis zu einer Tiefe von 1000. - Namen dürfen aus Buchstaben, Ziffern, `_`, `-` und `.` bestehen (nicht mit einer Ziffer oder `-` beginnend). Funktionen werden auch mit Tab vervollständigt. ### Positionsparameter | Parameter | Bedeutung | |--------------------|------------------------------------------------------------------| | `$0` | Name des Skripts (bzw. `name` bei `gosh -c befehl name`) | | `$1` … `$9`, `${10}` | die Argumente | | `$#` | Anzahl der Argumente | | `"$@"` | alle Argumente, jedes als eigenes Wort | | `"$*"` | alle Argumente als ein Wort, durch Leerzeichen getrennt | | `$$` | Prozess-ID der Shell | ```bash #!/usr/bin/gosh # aufruf: skript.gosh [-v] datei ... if [ $# -eq 0 ]; then echo "usage: $0 [-v] datei ..." >&2 exit 2 fi verbose=no case $1 in -v) verbose=yes; shift ;; esac for f in "$@"; do test $verbose = yes && echo "verarbeite $f" wc -l "$f" done ``` - Gesetzt werden sie von `gosh skript a b`, `gosh -c befehl name a b`, beim Aufruf einer Funktion, mit `source datei a b` (nur während die Datei läuft) und mit `set -- a b c`. - `shift [n]` entfernt die ersten `n` (Standard: 1) Parameter, `$2` wird zu `$1` usw. - Wie bei Variablen ist ein nicht gesetzter Parameter ein Fehler: `$1` ohne Argumente meldet `1: unbound variable`. Für optionale Argumente `${1:-}` bzw. `${1:-standard}` verwenden oder vorher `$#` prüfen. - `"$@"` ohne Argumente ergibt kein Wort (auch nicht das leere). Ohne Anführungszeichen verhalten sich `$@` und `$*` wie `"$@"`, da gosh Werte nie an Leerzeichen teilt. ### read `read` liest eine Zeile von stdin und verteilt sie auf Variablen: ```bash read -p "Name? " name # Eingabe vom Terminal mit Prompt read vorname nachname < name.txt # erste Zeile einer Datei read -r zeile # Backslashes nicht auswerten while read -r zeile; do echo "$zeile"; done # in einem Skript: alle Zeilen von stdin ``` - `read name1 name2 …` teilt die Zeile an den Zeichen von `$IFS` (Standard: Leerzeichen, Tab, Zeilenumbruch). Jede Variable bekommt ein Feld, die letzte den Rest der Zeile. Bei zu wenigen Feldern werden die übrigen Variablen leer. Leerzeichen am Anfang und Ende werden entfernt. Andere `IFS`-Zeichen trennen genau ein Feld (`IFS=:` macht aus `a::b` die Felder `a`, ``, `b`). - Ohne Namen landet die ganze Zeile unverändert in `$REPLY`. - Ohne `-r` maskiert `\` das nächste Zeichen (`a\ b` bleibt ein Feld), und ein `\` am Zeilenende setzt die Zeile fort. `-p prompt` gibt den Prompt auf stderr aus, wenn stdin ein Terminal ist. - Der Status ist 0, oder 1 am Ende der Eingabe (die Variablen werden trotzdem mit dem Rest gesetzt). So endet `while read …` nach der letzten Zeile. - `read` liest Byte für Byte und damit nie mehr als eine Zeile, den Rest bekommen die nächsten Befehle. Ctrl+C bricht `read` in der interaktiven Shell ab (Status 130). - `read` verändert die Shell und lässt sich deshalb nicht als einfacher Befehl in einer Pipeline verwenden (`echo a | read x`, in `sh` wirkungslos). Umleitungen funktionieren: `read x < datei`, `read x <<< "$text"`, `while read l; do …; done < datei`. In einer Schleife in einer Pipe (`… | while read l; do …; done`) geht es auch, da die ganze Schleife in einer Subshell läuft. ### Variablen ```bash name=wert # Variable setzen (wie set name=wert) a=1 b=2 # mehrere auf einmal gruss="Hallo Welt" # Leerzeichen im Wert brauchen Anführungszeichen heute=$(date +%F) # Ausgabe eines Befehls PATH=~/bin:$PATH # ~ wird auch nach = und : ersetzt LANG=C sort datei # nur für diesen einen Befehl unset name # Variable entfernen echo $name ${name:-standard} ``` - Eine Zuweisung ist ein Wort `name=wert` am Anfang eines Befehls, bei dem `name` und `=` ohne Anführungszeichen stehen und `name` aus Buchstaben, Ziffern und `_` besteht (nicht mit einer Ziffer beginnend). `"x=1"`, `x\=1` und `echo x=1` sind keine Zuweisungen. - Steht danach kein Befehl, wird die Variable in der Shell gesetzt. Wie bei `set` und `export` sind in gosh alle Variablen auch für gestartete Programme sichtbar (es gibt keine reinen Shell-Variablen). - Steht ein Befehl danach (`LANG=C sort datei`), gilt die Zuweisung nur für diesen Befehl, die Shell behält den alten Wert. - Der Wert wird nicht in Wörter geteilt und nicht als Wildcard oder `{a,b}` expandiert: `x=$(ls)` speichert die ganze Ausgabe, `x=*.go` speichert `*.go`. Variablen, `$(...)` und `~` (am Anfang, nach `=` und nach `:`) werden ersetzt. - `$?` ist nach einer Zuweisung der Status des letzten `$(...)` darin, sonst 0. - Variablen in den Argumenten werden ersetzt, bevor die Zuweisung davor gilt: `x=1 echo $x` gibt nicht `1` aus, sondern meldet `x: unbound variable` (wenn `x` vorher nicht gesetzt war). - In einer Pipeline (`x=1 | cat`) ändert eine Zuweisung die Shell nicht. ### Befehlsersetzung `$(befehl)` wird durch die Ausgabe von `befehl` ersetzt: ``` $ echo "Heute ist $(date +%A)" Heute ist Montag $ kill $(pgrep -f mein-server) $ cd $(git rev-parse --show-toplevel) $ vim $(grep -l TODO *.go) $ echo "Dateien: $(ls | wc -l)" ``` - Der Befehl läuft in einer eigenen Subshell (einem neuen gosh-Prozess): `cd`, `set` oder `export` darin ändern die laufende Shell nicht. Die Subshell kennt die Funktionen und Positionsparameter der Shell. Er darf alles enthalten, was gosh kann (Pipes, `;`, `&&`, Umleitungen, verschachtelte `$(...)`), und liest von der gleichen Eingabe, Fehlermeldungen gehen auf stderr. - Abschließende Zeilenumbrüche der Ausgabe werden entfernt. - Ohne Anführungszeichen wird die Ausgabe an Leerzeichen, Tabs und Zeilenumbrüchen in mehrere Wörter geteilt, `"$(befehl)"` bleibt ein einziges Wort (mit den Zeilenumbrüchen darin). - Anders als in `sh` werden Wildcards in der Ausgabe nicht expandiert, und Zeichen wie `;` oder `|` darin sind normaler Text: Die Ausgabe ist immer nur Daten. - Besteht ein Befehl nur aus einer Ersetzung (`$(false)`), ist ihr Exit-Status `$?`, sonst zählt der Status des Befehls. Ctrl+C während der Ersetzung bricht die ganze Zeile ab. - In `'...'` oder mit `\$(` wird nichts ersetzt. Die alte Schreibweise mit Backticks (`` `befehl` ``) und Arithmetik `$((1+2))` gibt es nicht. ### Befehlslisten und Exit-Status Mehrere Befehle in einer Zeile werden wie in `sh` verknüpft: | Liste | Bedeutung | |------------|---------------------------------------------------| | `a ; b` | erst `a`, dann `b` | | `a && b` | `b` nur, wenn `a` erfolgreich war (Exit-Status 0) | | `a \|\| b` | `b` nur, wenn `a` fehlgeschlagen ist | `&&` und `||` werden von links nach rechts ausgewertet, `a && b || c` führt also `c` aus, wenn `a` oder `b` fehlschlägt. Variablen werden erst beim Ausführen des jeweiligen Befehls expandiert, `cd /tmp && echo $PWD` gibt also schon das neue Verzeichnis aus. `$?` (bzw. `${?}`) enthält den Exit-Status des letzten Befehls, `exit` ohne Argument beendet gosh mit diesem Status: ``` $ false; echo $? 1 $ gibtsnicht || echo "Status $?" gosh: gibtsnicht: command not found Status 127 ``` Schlägt ein Programm fehl, gibt gosh selbst keine Meldung mehr aus (das Programm meldet den Fehler selbst), nur bei Fehlern von gosh, z. B. wenn ein Befehl nicht gefunden wird. ### Klammer-Expansion Wie in bash erzeugt `{…}` aus einem Wort mehrere Wörter: | Ausdruck | ergibt | |-------------------|---------------------------------| | `a{b,c}d` | `abd acd` | | `file{,.bak}` | `file file.bak` | | `{a,b}{1,2}` | `a1 a2 b1 b2` | | `x{a,{b,c}}` | `xa xb xc` (verschachtelt) | | `{1..5}` | `1 2 3 4 5` | | `{5..1}` | `5 4 3 2 1` | | `{1..10..3}` | `1 4 7 10` (Schrittweite) | | `{01..10..4}` | `01 05 09` (mit führenden Nullen) | | `{a..e}` | `a b c d e` | ``` $ mkdir -p projekt/{src,doc,test} $ cp config.yaml{,.bak} # Sicherungskopie $ ls *.{go,md} $ touch log-{01..12}.txt ``` - Die Klammer-Expansion kommt zuerst, die Ergebnisse durchlaufen danach `~`, Variablen und Wildcards: `*.{go,md}` wird zu `*.go *.md`, `{${A},b}` zu `$A b`. - Ohne Komma (und ohne gültige Folge) bleiben Klammern stehen: `{a}`, `{}`, `{a..1}`. Auch `${VAR}` wird nicht angefasst. - In Anführungszeichen oder mit `\` davor sind die Zeichen normale Zeichen: `"{a,b}"`, `\{a,b\}`. Ein Komma lässt sich mit `\,` schützen: `{a\,b,c}` ergibt `a,b c`. - Bei einer Umleitung muss genau ein Wort herauskommen, `> {a,b}` meldet `ambiguous redirect`. - Ein Wort darf höchstens 100 000 Wörter erzeugen. ### Wildcards Wörter mit `*`, `?` oder `[...]` werden durch die passenden Dateinamen ersetzt, sortiert: | Muster | passt auf | |-----------|-----------------------------------------------------| | `*` | beliebig viele Zeichen (auch keine) | | `?` | genau ein Zeichen | | `[abc]` | eines der Zeichen `a`, `b`, `c` | | `[a-z]` | ein Zeichen aus dem Bereich | | `[!abc]` | ein Zeichen außer `a`, `b`, `c` (auch `[^abc]`) | ``` $ ls *.go main.go main_test.go $ cat logs/2026-*/error.log $ ls -d */ # nur Verzeichnisse ``` - Muster gelten pro Pfadteil, `*` passt nie auf `/`: `*/*.go` findet `.go`-Dateien eine Ebene tiefer. - Versteckte Dateien (mit `.` am Anfang) werden nur gefunden, wenn das Muster selbst mit `.` beginnt: `*` findet `.bashrc` nicht, `.*rc` schon. - Passt keine Datei, bleibt das Wort unverändert stehen (wie in `sh`), `ls *.xyz` meldet dann also `ls` den Fehler. - In Anführungszeichen oder mit `\` davor sind die Zeichen normale Zeichen: `echo "*"`, `echo '*.go'`, `echo \*`. - Werte von Variablen werden nicht als Muster behandelt (anders als in `sh`): Nach `P='*.go'` gibt `echo $P` wörtlich `*.go` aus. So kann ein Variablenwert nie unbemerkt auf viele Dateien passen. `echo $DIR/*.go` funktioniert dagegen, weil das `*` direkt in der Zeile steht. - Bei einer Umleitung muss das Muster genau eine Datei treffen (`> *.log`), sonst meldet gosh `ambiguous redirect`. ### Pipes und Umleitungen ``` $ ls | sort -r | head -3 $ make > build.log 2>&1 $ grep -c error < build.log ``` | Syntax | Bedeutung | |-----------------|------------------------------------------------------------| | `a \| b` | stdout von `a` wird stdin von `b` | | `< datei` | stdin aus `datei` lesen | | `> datei` | stdout in `datei` schreiben (wird überschrieben) | | `>> datei` | stdout an `datei` anhängen | | `2> datei` | stderr in `datei` schreiben (`2>>` hängt an) | | `2>&1` | stderr dorthin, wohin stdout gerade geht | | `>&2` | stdout dorthin, wohin stderr gerade geht | | `&> datei` | stdout und stderr in `datei` (`&>>` hängt an) | | `< datei` | ohne Befehl: legt `datei` an bzw. leert sie | - Umleitungen werden von links nach rechts ausgewertet: `befehl > log 2>&1` schreibt beides in `log`, `befehl 2>&1 > log` schickt stderr dorthin, wo stdout vorher hinging (z. B. in eine Pipe). - Unterstützt werden die Dateideskriptoren 0, 1 und 2. Vor `>` und `<` zählt nur eine Zahl ohne Anführungszeichen als Deskriptor (`2>f`, aber nicht `x2>f` oder `"2">f`). - Operatoren in Anführungszeichen oder mit `\` davor sind normale Zeichen: `echo "a|b" 'c>d' e\>f`. - Der Exit-Status einer Pipeline ist der des letzten Befehls. Beendet sich ein Befehl am Ende (z. B. `head`), bekommt der schreibende Befehl davor `SIGPIPE`, `yes | head -1` endet also sofort. - Die Built-ins `pwd` und `help` lassen sich umleiten und in Pipes verwenden (`help | less`). `cd`, `set`, `export`, `exit` und `quit` verändern die Shell selbst und sind in einer Pipeline nicht erlaubt. Umleitungen von `source datei > log` gelten für alle Befehle der Datei. - Zu Kontrollstrukturen mit Umleitungen und in Pipes siehe oben. ### Here-Docs und Here-Strings ```bash cat < config.ini [server] host = $HOSTNAME started = $(date +%F) EOF cat <<'EOF' > script.sh # 'EOF': nichts wird ersetzt echo "$HOME bleibt wörtlich stehen" EOF if true; then cat <<-EOF # <<-: Tabs am Zeilenanfang werden entfernt eingerückt EOF fi grep -c x <<< "$text" # Here-String read a b <<< "1 2" ``` - Nach `< `. - Ohne Anführungszeichen am Wort werden `$VAR`, `${VAR}`, `$(…)` und `\` vor `$`, `` ` ``, `\` und dem Zeilenende ersetzt (wie in `"…"`, nur dass `"` ein normales Zeichen ist). Wildcards, `~` und `{a,b}` werden nicht ersetzt. Ist das Wort ganz oder teilweise in Anführungszeichen (`'EOF'`, `"EOF"`, `\EOF`), wird nichts ersetzt. - `<<-WORT` entfernt Tabs (keine Leerzeichen) am Anfang jeder Zeile und der Endzeile, damit sich Here-Docs in Skripten einrücken lassen. - Mehrere Here-Docs in einer Zeile (`cat <