.bashrc, .bash_profile und .profile: die Unterschiede wirklich verstehen
AI generated
$_
#!/
Bash · Shell-Konfiguration · Login-Shell · Deployment
.bashrc, .bash_profile und .profile
Welche Datei geladen wird, haengt vom Shell-Typ ab, nicht vom Zufall

Bash laedt bei jedem Start eine unterschiedliche Kombination aus Konfigurationsdateien, abhaengig davon, ob es sich um eine Login-Shell, eine interaktive Nicht-Login-Shell oder eine Skriptausfuehrung ohne Interaktion handelt. Wer PATH-Erweiterungen oder Umgebungsvariablen in die falsche Datei schreibt, bekommt lokal ein scheinbar funktionierendes Setup und wundert sich, warum genau dieselbe Variable im Cronjob oder Deployment-Skript plötzlich fehlt.

16 Min. Lesezeit Login-Shell · interaktive Shell · PATH Bash 4.x · 5.x · macOS · Linux

1. Drei Shell-Arten, drei unterschiedliche Ladepfade

Bash unterscheidet beim Start grundsaetzlich zwischen drei Betriebsarten, die jeweils eine andere Kombination von Konfigurationsdateien laden: eine Login-Shell, die typischerweise beim Anmelden per SSH oder an einer Text-Konsole entsteht, eine interaktive Nicht-Login-Shell, wie sie ein neues Terminal-Fenster auf dem Desktop oeffnet, und eine nicht-interaktive Shell, die ein Skript ohne jede Benutzerinteraktion ausfuehrt, etwa aus einem Cronjob oder Deployment-Prozess heraus.

Diese Unterscheidung ist kein akademisches Detail, sondern die Ursache fuer einen der haeufigsten Bash-Fehler ueberhaupt: eine Umgebungsvariable oder ein PATH-Eintrag, der beim interaktiven Arbeiten im Terminal zuverlaessig funktioniert, aber in einem automatisierten Skript spurlos fehlt, weil dieses Skript als nicht-interaktive Shell laeuft und eine ganz andere Konfigurationsdatei laedt, oder gar keine.

2. .bash_profile und .profile: wann Login-Shells sie laden

Startet Bash als Login-Shell, sucht es in dieser Reihenfolge nach genau einer der folgenden Dateien und fuehrt nur die erste gefundene aus: /etc/profile zuerst systemweit, danach vom Benutzer aus ~/.bash_profile, falls diese Datei existiert, sonst ~/.bash_login, und erst wenn auch diese fehlt, ~/.profile. Diese Kaskade bedeutet, dass eine vorhandene ~/.bash_profile eine vorhandene ~/.profile vollstaendig verdeckt, was bei Migrationen zwischen Systemen leicht zu Verwirrung fuehrt, wenn Einstellungen in der ignorierten Datei landen.

~/.profile ist dabei die shell-uebergreifende Variante, die auch von sh, dash oder ksh gelesen wird und deshalb keine bash-spezifische Syntax wie Arrays oder [[ ]]-Tests enthalten sollte, waehrend ~/.bash_profile explizit nur fuer Bash gilt und bash-spezifische Konstrukte gefahrlos nutzen darf. Fuer reine PATH-Erweiterungen und Umgebungsvariablen, die auch von anderen Shells verstanden werden sollen, ist ~/.profile deshalb oft die portablere Wahl.


# ~/.bash_profile -- loaded ONCE per login shell (SSH login, tty login)
# Good place for PATH extensions and env vars that should exist for the
# entire session, including any non-interactive scripts started from it.

export PATH="$HOME/.local/bin:$PATH"
export EDITOR="vim"

# Source .bashrc explicitly, since login shells do NOT load it automatically
if [ -f "$HOME/.bashrc" ]; then
  . "$HOME/.bashrc"
fi

3. .bashrc: wann interaktive Nicht-Login-Shells sie laden

Startet Bash dagegen als interaktive, aber nicht als Login-Shell, etwa beim Oeffnen eines neuen Terminal-Tabs in einer bereits laufenden grafischen Desktop-Sitzung, wird stattdessen ~/.bashrc geladen, und zwar ausschliesslich diese Datei, nicht ~/.bash_profile. Genau hier gehoeren deshalb alle Einstellungen hin, die nur fuer die interaktive Nutzung Sinn ergeben: Aliase, die Prompt-Definition PS1, Shell-Optionen wie shopt, und Autocomplete-Erweiterungen.

Der Grund fuer diese Trennung liegt historisch darin, dass eine Login-Shell traditionell eine langsamere, einmalige Initialisierung durchlaufen durfte, waehrend .bashrc bei jedem neu geoeffneten Terminal-Fenster erneut ausgefuehrt wird und deshalb schlank bleiben sollte. Wer rechenintensive Befehle wie Netzwerkabfragen in .bashrc platziert, spuert das bei jedem neuen Terminal-Fenster als spuerbare Verzoegerung, waehrend dieselbe Operation in .bash_profile nur einmal pro Sitzung kostet.


# ~/.bashrc -- loaded on EVERY new interactive, non-login shell
# (new terminal tab, `bash` invoked manually). Good place for aliases,
# prompt customization, and shell options -- NOT for PATH/env vars that
# other tools (cron, systemd, deploy scripts) might also need.

alias ll="ls -alF"
alias gs="git status"

PS1='\u@\h:\w\$ '
shopt -s histappend
shopt -s checkwinsize

4. Warum .bash_profile oft .bashrc explizit nachlaedt

Weil eine Login-Shell .bashrc nicht automatisch mitlaedt, faellt bei einer neuen SSH-Verbindung ohne Zusatzmassnahme genau die Datei mit Aliasen und Prompt-Definition weg, obwohl eine SSH-Sitzung fuer den Benutzer voellig normal und interaktiv wirkt. Deshalb hat sich als de-facto-Standard etabliert, dass ~/.bash_profile am Ende explizit ~/.bashrc per source beziehungsweise dem Kurzbefehl . nachlaedt, wie im Codebeispiel des vorherigen Abschnitts gezeigt.

Diese Kette sorgt dafuer, dass sich eine SSH-Login-Sitzung fuer den Benutzer wie eine normale interaktive Shell anfuehlt, mit allen gewohnten Aliasen und dem gewohnten Prompt, obwohl technisch zwei getrennte Dateien in einer bestimmten Reihenfolge geladen werden. Wird diese Nachlade-Zeile aus .bash_profile entfernt oder versehentlich geloescht, verschwinden bei der naechsten SSH-Anmeldung scheinbar grundlos alle Aliase, ohne dass sich sonst etwas an der Konfiguration geaendert haette.

5. Plattform-Unterschiede: macOS Terminal.app versus Linux-Desktop

Ein haeufiger Verwirrungspunkt zwischen Betriebssystemen ist, dass macOS' Terminal.app traditionell jedes neue Fenster als Login-Shell startet, waehrend die meisten Linux-Desktop-Terminalemulatoren wie GNOME Terminal oder Konsole neue Fenster standardmaessig als interaktive Nicht-Login-Shell oeffnen. Ein Setup, das unter macOS problemlos funktioniert, weil ~/.bash_profile bei jedem neuen Fenster laeuft, kann unter Linux fehlschlagen, wenn dieselben Einstellungen versehentlich nur in .bash_profile statt zusaetzlich in .bashrc stehen.

Diese Diskrepanz erklaert auch, warum viele plattformuebergreifende Dotfile-Repositories beide Dateien pflegen und .bash_profile konsequent .bashrc nachlaedt: So wird sichergestellt, dass dieselbe Konfiguration unabhaengig davon greift, ob ein neues Terminal-Fenster als Login-Shell oder als interaktive Nicht-Login-Shell startet, ohne dass der Nutzer sich um die plattformspezifischen Unterschiede kuemmern muss.

6. Nicht-interaktive Skripte: warum sie standardmaessig gar nichts laden

Fuehrt Bash ein Skript aus, etwa mit ./deploy.sh oder als Cronjob-Eintrag, laeuft die Shell weder als Login-Shell noch als interaktive Shell, sondern als nicht-interaktive Shell. In diesem Modus laedt Bash standardmaessig ueberhaupt keine der genannten Dateien automatisch, mit einer Ausnahme: Ist die Umgebungsvariable BASH_ENV gesetzt, wird die darin referenzierte Datei geladen, bevor das Skript startet, ein Mechanismus, der in der Praxis selten genutzt wird, aber existiert.

Das erklaert das klassische Cronjob-Problem: Ein Alias oder eine PATH-Erweiterung, die in ~/.bashrc definiert ist und beim manuellen Testen im Terminal einwandfrei funktioniert, existiert im Cronjob schlicht nicht, weil der Cron-Daemon Skripte als nicht-interaktive Shells ohne Login startet und keine der interaktiven Konfigurationsdateien laedt. Ein Skript, das auf einen per Alias definierten Kurzbefehl oder eine nur in .bashrc gesetzte PATH-Erweiterung angewiesen ist, schlaegt im Cronjob mit 'command not found' fehl, obwohl es im Terminal tadellos laeuft.


# crontab -e
# This entry runs as a non-interactive, non-login shell -- neither
# .bashrc nor .bash_profile is loaded automatically.
0 3 * * * /opt/scripts/nightly-backup.sh >> /var/log/backup.log 2>&1

# nightly-backup.sh MUST set its own PATH and env vars explicitly,
# it cannot rely on anything defined only in ~/.bashrc
#!/usr/bin/env bash
set -euo pipefail
export PATH="/usr/local/bin:/usr/bin:/bin"
export DATABASE_URL="postgres://backup_user@localhost/app"

7. Typischer Fehler: einen Alias in .bashrc definieren und im Skript erwarten

Ein besonders haeufiges Missverstaendnis ist die Annahme, ein in .bashrc definierter Alias stehe automatisch auch in jedem gestarteten Skript zur Verfuegung. Selbst wenn ein Skript versehentlich ~/.bashrc explizit per source nachlaedt, greifen Aliase in nicht-interaktiven Shells standardmaessig trotzdem nicht, weil Bash die Alias-Expansion in Skripten aus Sicherheits- und Konsistenzgruenden deaktiviert, sofern nicht explizit shopt -s expand_aliases gesetzt wird.

Die robuste Loesung ist grundsaetzlich, in Skripten niemals Aliase zu verwenden, sondern stattdessen vollstaendige Funktionsdefinitionen oder direkte Befehlsaufrufe, weil Funktionen im Gegensatz zu Aliasen auch in nicht-interaktiven Shells und selbst nach dem Export mit export -f an Kindprozesse zuverlaessig funktionieren. Diese klare Trennung zwischen 'praktische Abkuerzung fuer den Menschen im Terminal' und 'zuverlaessiger Baustein fuer ein Skript' vermeidet die haeufigste Fehlerquelle bei diesem Thema von vornherein.

8. Warum Deployment-Skripte sich nicht auf ~/.bashrc verlassen sollten

Ein Deployment-Skript, das PATH-Erweiterungen, Versionsmanager wie nvm oder Umgebungsvariablen aus ~/.bashrc voraussetzt, funktioniert zuverlaessig nur, solange es interaktiv von einem Menschen im Terminal gestartet wird. Sobald derselbe Prozess von einer CI-Pipeline, einem systemd-Service oder einem Cronjob ausgeloest wird, laeuft er garantiert als nicht-interaktive Shell, in der .bashrc nie geladen wird, und das Skript bricht mit unklaren 'command not found'-Fehlern ab, die je nach Ausloeser unterschiedlich und schwer reproduzierbar wirken.

Die belastbare Loesung ist, jedes Deployment-Skript so zu schreiben, dass es alle benoetigten PATH-Eintraege und Umgebungsvariablen am eigenen Skriptanfang explizit selbst setzt, statt sich implizit auf eine geerbte interaktive Shell-Umgebung zu verlassen. Diese Eigenstaendigkeit macht das Skript gleichzeitig portabler: Es laeuft dann identisch, egal ob es manuell im Terminal, von cron, von systemd oder aus einer CI-Pipeline heraus gestartet wird.


#!/usr/bin/env bash
set -euo pipefail

# Never assume ~/.bashrc has run -- set everything the script needs explicitly.
export PATH="/usr/local/bin:$HOME/.local/bin:$PATH"
export NODE_ENV="production"

# If a version manager is truly required, source it explicitly with a
# guard, rather than assuming it was already initialized by .bashrc
if [ -s "$HOME/.nvm/nvm.sh" ]; then
  . "$HOME/.nvm/nvm.sh"
  nvm use 20 --silent
fi

npm run build

9. Die Konfigurationsdateien im direkten Vergleich

Wer die vier Dateien und ihre jeweiligen Ladebedingungen einmal klar nebeneinander sieht, vermeidet die meisten der oben beschriebenen Fallstricke von vornherein, weil sofort ersichtlich wird, welche Datei fuer welchen konkreten Anwendungsfall gedacht ist und welche fuer Automatisierung schlicht ungeeignet ist.

Datei Wird geladen bei Empfohlener Inhalt Fuer Automatisierung geeignet
/etc/profile Jede Login-Shell, systemweit Globale PATH-Basis, Systemvariablen Nein, gilt nur fuer Login-Shells
~/.bash_profile Login-Shell (SSH, tty) PATH, Umgebungsvariablen, .bashrc nachladen Nein, gilt nur fuer Login-Shells
~/.bashrc Interaktive Nicht-Login-Shell Aliase, PS1, shopt, Autocomplete Nein, wird bei Skripten nicht geladen
~/.profile Login-Shell, falls .bash_profile fehlt Portable, shell-uebergreifende Einstellungen Nein, gilt nur fuer Login-Shells

Mironsoft

Shell-Automatisierung, DevOps-Tooling und Deployment-Infrastruktur

Shell-Skripte, die in der Produktion zuverlässig laufen?

Wir analysieren bestehende Bash-Skripte, erkennen fragile Muster und ersetzen sie durch robuste Bash-Patterns: mit vollständiger Fehlerbehandlung, Logging und sicherer Parallelisierung für euren Deployment-Stack.

Code-Review

ShellCheck-Analyse und manuelle Prüfung auf kritische Bash-Pattern-Verstöße.

Refactoring

Fehlerbehandlung, Logging und sichere Dateioperationen nachrüsten.

CI-Integration

ShellCheck und BATS in Pipelines integrieren und Regressionstests aufbauen.

10. Zusammenfassung

.bashrc, .bash_profile und .profile: Das Wichtigste auf einen Blick

Login-Shell

Laedt genau eine Datei aus /etc/profile, ~/.bash_profile, ~/.bash_login oder ~/.profile, in dieser Reihenfolge.

Interaktive Shell

Laedt ausschliesslich ~/.bashrc, ideal fuer Aliase, Prompt und Shell-Optionen fuer die tägliche Terminal-Nutzung.

Nicht-interaktive Shell

Laedt standardmaessig keine dieser Dateien, ausser BASH_ENV ist explizit gesetzt. Skripte muessen PATH und env selbst setzen.

Deployment-Regel

PATH-Eintraege, Versionsmanager und Umgebungsvariablen im Skript selbst setzen, niemals implizit auf .bashrc verlassen.

11. FAQ: .bashrc, .bash_profile und .profile: Das Wichtigste auf einen Blick

1Was ist der Unterschied zwischen .bashrc und .bash_profile?
.bashrc wird bei jeder interaktiven Nicht-Login-Shell geladen, etwa einem neuen Terminal-Fenster. .bash_profile wird nur bei einer Login-Shell geladen, etwa einer SSH-Anmeldung, und in der Regel nur einmal pro Sitzung.
2Warum laedt eine SSH-Sitzung meine Aliase, obwohl sie in .bashrc stehen?
Weil .bash_profile ueblicherweise am Ende explizit .bashrc per source nachlaedt. Ohne diese Zeile wuerden Aliase bei einer SSH-Login-Sitzung tatsaechlich fehlen.
3Wann wird .profile statt .bash_profile geladen?
.profile wird nur geladen, wenn weder .bash_profile noch .bash_login existieren. Existiert .bash_profile, wird .profile komplett ignoriert.
4Warum funktioniert mein Alias im Cronjob nicht, obwohl er im Terminal funktioniert?
Cronjobs laufen als nicht-interaktive Shells, die weder .bashrc noch .bash_profile automatisch laden. Aliase und dort gesetzte PATH-Eintraege existieren im Cronjob schlicht nicht.
5Welche Datei laedt eine nicht-interaktive Shell wie ein Skript?
Standardmaessig keine der genannten Dateien. Einzige Ausnahme ist eine gesetzte BASH_ENV-Umgebungsvariable, deren referenzierte Datei vor dem Skript geladen wird.
6Warum ist macOS Terminal.app anders als Linux-Terminals?
macOS Terminal.app startet neue Fenster traditionell als Login-Shell und laedt .bash_profile. Die meisten Linux-Desktop-Terminals oeffnen neue Fenster als interaktive Nicht-Login-Shell und laden .bashrc.
7Sollte ich PATH-Erweiterungen in .bashrc oder .bash_profile setzen?
In .bash_profile, weil diese Datei bei einer Login-Sitzung einmalig laeuft und die Variable dann an alle daraus gestarteten Kindprozesse vererbt wird. In .bashrc gesetzte Variablen gelten nur fuer diese eine interaktive Shell.
8Warum sollten Deployment-Skripte sich nicht auf .bashrc verlassen?
Weil Deployment-Skripte oft als nicht-interaktive Shell von CI-Pipelines, systemd oder Cron gestartet werden, wo .bashrc nie geladen wird. PATH und Umgebungsvariablen muessen deshalb im Skript selbst gesetzt werden.
9Funktionieren Aliase in Bash-Skripten?
Nicht ohne Weiteres. Bash deaktiviert Alias-Expansion in nicht-interaktiven Shells standardmaessig, sofern nicht explizit shopt -s expand_aliases gesetzt wird. Funktionen sind in Skripten die zuverlaessigere Alternative.
10Was passiert, wenn sowohl .bash_profile als auch .profile existieren?
Bash fuehrt bei einer Login-Shell nur die erste gefundene Datei in der Reihenfolge .bash_profile, .bash_login, .profile aus. Existiert .bash_profile, wird .profile vollstaendig ignoriert.