add subshells, here-documents and redirections/pipes for control structures
- control structures can have redirections (while read l; do ...; done
< file), which apply to all commands in them; they run in the shell
- in pipes and in the background they run in a subshell (cmd | while
read l; do ...; done, for ...; done &, a && b &)
- ( list ) subshells; ( and ) are operators now
- here-documents <<EOF, <<-EOF, <<'EOF' and here-strings <<<word
- redirections of source apply to the commands of the file
- fix non-ASCII characters in quotes being mangled ('für')
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
443367f66f
commit
f66cf538c0
7 files changed
+1172
-221
No files matched your search
@@ -67,12 +67,16 @@ Features:
|
||||
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 negates the status; they can span several lines (prompt "> ")
|
||||
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
|
||||
2>> file, 2>&1, >&2, &> file (stdout and stderr), &>> file,
|
||||
<<EOF here-documents (<<-EOF strips tabs, <<'EOF' expands nothing),
|
||||
<<<word here-strings
|
||||
- Job control: command & runs in the background, Ctrl+Z stops the
|
||||
foreground job; jobs, fg, bg and wait manage the jobs
|
||||
- Aliases replace the first word of a command in interactive shells
|
||||
@@ -151,7 +155,7 @@ export PATH=$HOME/bin:$PATH
|
||||
|
||||
### Kontrollstrukturen
|
||||
|
||||
gosh kennt `if`, `for`, `while`, `until`, `case` und `{ …; }` wie `sh`, auf einer Zeile oder über mehrere Zeilen:
|
||||
gosh kennt `if`, `for`, `while`, `until`, `case`, `{ …; }` und `( … )` wie `sh`, auf einer Zeile oder über mehrere Zeilen:
|
||||
|
||||
```bash
|
||||
if [ -f ~/.goshrc ]; then
|
||||
@@ -194,6 +198,7 @@ test -d build || { echo "kein build-Verzeichnis"; exit 1; }
|
||||
| `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`) |
|
||||
@@ -206,7 +211,50 @@ test -d build || { echo "kein build-Verzeichnis"; exit 1; }
|
||||
- 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.
|
||||
- Nicht unterstützt: Kontrollstrukturen in Pipes (`for …; done | sort`, `… | while read line`), mit Umleitung (`done > datei`, `done < datei`) oder im Hintergrund (`done &`), außerdem `( … )`-Subshells. Funktionen lassen sich dagegen in Pipes und mit Umleitungen verwenden (siehe unten), damit lässt sich das oft umgehen. Arithmetik geht z. B. mit `n=$(expr $n + 1)`.
|
||||
- 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
|
||||
|
||||
@@ -229,11 +277,12 @@ ist_root() {
|
||||
}
|
||||
ist_root || echo "kein root"
|
||||
|
||||
# Zeilen einer Datei verarbeiten: die Funktion liest von stdin
|
||||
# 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.
|
||||
@@ -295,7 +344,7 @@ while read -r zeile; do echo "$zeile"; done # in einem Skript: alle Zeilen von
|
||||
- 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 in einer Pipeline verwenden (`echo a | read x`, in `sh` wirkungslos). Umleitungen funktionieren: `read x < datei`.
|
||||
- `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
|
||||
|
||||
@@ -435,13 +484,47 @@ $ grep -c error < build.log
|
||||
| `2>&1` | stderr dorthin, wohin stdout gerade geht |
|
||||
| `>&2` | stdout dorthin, wohin stderr gerade geht |
|
||||
| `&> datei` | stdout und stderr in `datei` (`&>>` hängt an) |
|
||||
| `<<EOF` | Here-Doc: die folgenden Zeilen bis `EOF` sind stdin |
|
||||
| `<<< wort` | Here-String: `wort` (und ein Zeilenumbruch) ist stdin |
|
||||
| `> 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.
|
||||
- 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 <<EOF > 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 `<<WORT` folgen ab der nächsten Zeile die Eingabezeilen, bis zu einer Zeile, die genau `WORT` ist. In der interaktiven Shell zeigt gosh dafür den Fortsetzungs-Prompt `> `.
|
||||
- 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 <<A; cat <<B`) folgen nacheinander. Here-Docs gehen auch an Funktionen, Schleifen (`done <<EOF`) und in Pipes.
|
||||
- `<<< wort` übergibt `wort` mit einem Zeilenumbruch als Eingabe. Das Wort wird wie ein Argument expandiert (Variablen, `$(…)`), aber nicht als Wildcard.
|
||||
- Die Eingabe steht in einer temporären Datei, die sofort wieder gelöscht wird, deshalb sind auch große Here-Docs kein Problem.
|
||||
- Ein Here-Doc in einer Befehlsersetzung (`x=$(cat <<EOF … EOF)`) funktioniert, solange der Text darin keine unpaarigen Anführungszeichen wie `it's` enthält.
|
||||
|
||||
### Aliase
|
||||
|
||||
|
||||
Reference in new issue
Block a user