Allgemeines zur Shell

Ausgangslage: Interaktive Shell ist Fish. Die folgenden sechs Versionen des selben Skripts werden ausgeführt, um die Unterschiede herauszuarbeiten.

#!/bin/fish
please nala update;
please nala upgrade -y;
please nala full-upgrade -y;
please nala autoremove -y;
please nala autopurge -y;
echo '';
ffplay -nodisp -autoexit -loglevel quiet ~/skripte/baf/Burp.mp3
echo '$> done.';
echo '';

# funktioniert
#!/bin/bash
please nala update;
please nala upgrade -y;
please nala full-upgrade -y;
please nala autoremove -y;
please nala autopurge -y;
echo '';
ffplay -nodisp -autoexit -loglevel quiet ~/skripte/baf/Burp.mp3
echo '$> done.';
echo '';

# bash kennt please nicht
#!/bin/fish
sudo nala update;
sudo nala upgrade -y;
sudo nala full-upgrade -y;
sudo nala autoremove -y;
sudo nala autopurge -y;
echo '';
ffplay -nodisp -autoexit -loglevel quiet ~/skripte/baf/Burp.mp3
echo '$> done.';
echo '';

# funktioniert
#!/bin/bash
sudo nala update;
sudo nala upgrade -y;
sudo nala full-upgrade -y;
sudo nala autoremove -y;
sudo nala autopurge -y;
echo '';
ffplay -nodisp -autoexit -loglevel quiet ~/skripte/baf/Burp.mp3
echo '$> done.';
echo '';

# bislang beste Version
#!/bin/fish
nala update;
nala upgrade -y;
nala full-upgrade -y;
nala autoremove -y;
nala autopurge -y;
echo '';
ffplay -nodisp -autoexit -loglevel quiet ~/skripte/baf/Burp.mp3
echo '$> done.';
echo '';

# es fehlt an root-Rechten
# sollte das Skript mit root-Rechten ausgeführt werden, kann ffplay nicht ausgeführt werden, weil das im Userkontext laufen muss.
#!/bin/bash
nala update;
nala upgrade -y;
nala full-upgrade -y;
nala autoremove -y;
nala autopurge -y;
echo '';
ffplay -nodisp -autoexit -loglevel quiet ~/skripte/baf/Burp.mp3
echo '$> done.';
echo '';

# es fehlt an root-Rechten
# sollte das Skript mit root-Rechten ausgeführt werden, kann ffplay nicht ausgeführt werden, weil das im Userkontext laufen muss.

Die Kernprobleme in diesem Fall sind:

  1. Aliase gelten nicht in nicht-interaktiven Shells (also in Skripten).
  2. please ist ein Alias, kein eigenständiges Programm.
  3. bash und fish behandeln Aliase und Funktionen völlig unterschiedlich – sie teilen sich keine Alias-Definitionen.

Das heißt:

  • Bei #!/bin/fish funktioniert please, weil fish das Alias kennt (please.fish).
  • Bei #!/bin/bash kann please nicht funktionieren, denn bash kennt das Alias nicht, weil es in der .bashrc nicht existiert (und selbst wenn, wird die bei Skriptaufruf sowieso nicht geladen!).
  • Wenn das ganze Skript mit please ./skript.sh gestartet wird, läuft alles als root, auch der ffmpeg-Aufruf – und ffmpeg kann dann u. U. keine Audiosession starten (weil der root-User z.B. keinen Zugriff auf den PulseAudio- oder PipeWire-Sessionbus des Users hat).

Fazit:

  • Der Gedanke, bash als Skriptbasis zu behalten und fish als interaktive Shell zu nutzen, ist eindeutig die sauberste Lösung:
    • Systemweite Kompatibilität bleibt gewahrt.
    • Alias-Abhängigkeiten werden vermieden.
    • Klare Trennung zwischen „interaktivem Komfort“ (fish) und „systemweiter Kompatibilität“ (bash).

Das bedeutet konkret:
#!/bin/bash in allen Skripten behalten.
please überall entfernen und, wenn nötig, direkt sudo verwenden.


Und wenn man „please“ einfach generell verfügbar machen würde?

Wenn du unbedingt das please-Feeling in bash willst, dann leg dir ein winziges Wrapper-Skript an:

#!/bin/bash
exec sudo "$@"

und speichere das als /usr/local/bin/please.
Dann noch mit chmod a+x ausführbar machen.
Damit ist please ein echtes Programm und funktioniert dann auch in bash-Skripten, Cronjobs usw.

Ja aber

beißt sich das nicht mit meiner Funktion „please.fish„, weil etwas in /usr/local/bin ja auch systemweit und unabhängig von der Shell einsetzbar wäre?

Nein, das Stichwort heißt Kommandoauflösungsverfahren (englisch: command resolution oder command lookup order).

🧩 Allgemeines Prinzip (vereinfachte Darstellung)

  1. Shell-eigene Schlüsselwörter / Syntaxelemente
    → z. B. if, for, while, function, [[ ... ]]
    Diese sind Teil der Sprache selbst, keine Kommandos.
  2. Aliase (bash, zsh) oder Abbreviations (fish)
    → werden rein textuell ersetzt, bevor der Befehl überhaupt interpretiert wird.
  3. Shell-Funktionen
    → benutzerdefinierte Funktionen, die in der aktuellen Session bekannt sind.
    Diese haben Vorrang vor externen Programmen gleichen Namens.
  4. Shell-Builtins
    → interne Befehle, die direkt im Shell-Prozess implementiert sind
    (z. B. cd, echo, test, export, read).
  5. Externe Programme
    → in $PATH gesucht, beginnend beim ersten Eintrag (meist /usr/local/bin, /usr/bin, …).
    Der erste Treffer gewinnt.
  6. (Optional) Funktionen oder Hooks aus Plugins / Completion-Systemen
    → z. B. in fish oder zsh kann es zusätzliche Lookup-Schichten geben.

Reihenfolge bei Bash

Bash beschreibt das, laut man bash, Abschnitt COMMAND EXECUTION, so:

  1. Alias expansion
  2. Function lookup
  3. Builtin lookup
  4. $PATH search

Reihenfolge bei Fish

Fish macht es etwas moderner und konsistenter:

  1. Abbreviations (Abkürzungen) – werden beim Tippen expandiert, nicht zur Laufzeit.
  2. Functions – alle Funktionen in ~/.config/fish/functions/ oder geladenen Plugins.
  3. Builtins – interne Kommandos (set, cd, status, …).
  4. Externe Kommandos – gesucht in $PATH (Reihenfolge der PATH-Einträge).
  5. Implicit cd – falls kein Kommando gefunden wird, aber ein Ordner gleichen Namens existiert, führt fish cd dorthin aus (nur in interaktiven Sessions).