Wie eine Shell hat Vim ein Working Directory — den Kontext, von dem aus relative Pfade interpretiert werden, in dem :!command ausgeführt wird, in dem :e file.txt ohne Pfad sucht. Was viele Anfänger nicht wissen: Vim erlaubt drei Ebenen dieses Konzepts. Es gibt ein globales Working Directory, ein window-lokales und ein tab-lokales. Sauber genutzt entstehen daraus elegante Workflows: Frontend-Tab im Frontend-Verzeichnis, Backend-Tab im Backend-Verzeichnis, beide unabhängig — und jeder Split innerhalb kann sein eigenes Working Directory haben. Dazu kommt der autochdir-Mechanismus, der das Working Directory automatisch der aktuellen Datei folgen lässt, und Plugin-basierte Projekt-Root-Detection. Dieser Artikel zeigt alle drei Ebenen, das Auto-Verhalten und die typischen Konfigurations-Optionen.
Wo bin ich gerade? :pwd
Der einfachste Befehl zur Orientierung:
:pwd/home/user/projects/my-siteVim zeigt das aktuell wirksame Working Directory — und das ist mehr als „wo Vim gestartet wurde". Wenn ein window-lokales oder tab-lokales Verzeichnis gesetzt ist, zeigt :pwd das wirksame Verzeichnis aus Sicht des aktuellen Fensters.
Bei einem frischen Vim-Start ist das Working Directory das Verzeichnis, in dem du Vim aufgerufen hast — analog zur Shell. Mit vim foo.txt im Verzeichnis ~/projects/site/ wird ~/projects/site/ zum initialen Vim-Working-Directory, unabhängig davon, wo foo.txt tatsächlich liegt.
Drei Ebenen: :cd, :lcd, :tcd
Vim unterscheidet drei Ebenen des Working Directory. Alle drei haben eigene :cd-Varianten:
| Befehl | Wirkung |
|---|---|
:cd path | globales Working Directory wechseln — gilt für ALLE Fenster und Tabs (sofern nicht überschrieben) |
:lcd path | window-lokales Working Directory — nur für das aktuelle Fenster |
:tcd path | tab-lokales Working Directory — für alle Fenster im aktuellen Tab |
:cd - | zurück zum vorherigen Working Directory |
:cd ~ | zum Home-Verzeichnis |
:cd %:h | zum Verzeichnis der aktuellen Datei |
Die Auflösungs-Reihenfolge ist:
- Wenn ein window-lokales Verzeichnis gesetzt ist (per
:lcd), gilt es. - Sonst, wenn ein tab-lokales Verzeichnis gesetzt ist (per
:tcd), gilt es. - Sonst gilt das globale Verzeichnis.
Diese Hierarchie erlaubt sehr feine Setup-Varianten:
" Vim-Start in ~/projects/myapp/
:pwd " /home/user/projects/myapp
" Tab 1: bleibt im Standard-Verzeichnis
" Tab 2: explizit für Frontend-Arbeit
:tabnew
:tcd frontend/
:pwd " .../myapp/frontend
" Tab 3: für Backend
:tabnew
:tcd backend/
" In Tab 2 einen Fenster-Split mit anderem WD:
:vsplit
:lcd ~/.config/vim/ " nur dieses Fenster ist hier
:pwd " ~/.config/vim
" Der ander Split in Tab 2 bleibt in frontend/Jedes :e foo.txt, jedes :!ls, jede Tab-Completion bezieht sich nun auf das wirksame Working Directory des jeweiligen Fensters/Tabs. Eine elegante Trennung, die in Vim umgesetzt ist, in den meisten anderen Editoren aber fehlt.
autochdir — automatisch ins Datei-Verzeichnis wechseln
Wer es nicht mag, das Working Directory manuell zu pflegen, kann Vim per Option dazu bringen, automatisch ins Verzeichnis der aktuellen Datei zu wechseln, sobald sie geöffnet wird:
" Wechsle automatisch ins Verzeichnis der aktiven Datei.
" Bei jedem Buffer-Wechsel passt sich das WD an.
set autochdirVorteil: relative Pfade in :e, :r, :!command beziehen sich automatisch auf den Ort der aktuellen Datei. Wenn man in src/foo.go arbeitet und :e bar.go tippt, wird src/bar.go geöffnet — ohne den Pfad ausschreiben zu müssen.
Nachteil: man verliert den Kontext eines Projekt-Roots. :!grep -r pattern . wird nur im Datei-Verzeichnis suchen, nicht im gesamten Projekt. Auch Plugins, die das Working Directory zur Projekt-Erkennung nutzen, kommen ins Trudeln.
Aus diesen Gründen ist autochdir in der Praxis polarisierend. Viele User schalten es nicht ein und nutzen stattdessen explizite :cd-Befehle oder ein Projekt-Root-Plugin.
Projekt-Root-Detection ohne autochdir
Eine elegantere Alternative zu autochdir ist die Projekt-Root-Detection: Vim wechselt nicht ins Datei-Verzeichnis, sondern in das Wurzel-Verzeichnis des Projekts (typischerweise das Verzeichnis mit der .git/-Datei, einer package.json, einem go.mod etc.).
Eigene Implementierung in Vimscript
" Suche aufwärts nach Projekt-Marker und setze WD entsprechend.
function! s:FindProjectRoot() abort
let markers = ['.git', 'go.mod', 'package.json', 'Cargo.toml', 'pyproject.toml']
for marker in markers
let path = finddir(marker, expand('%:p:h') . ';')
if !empty(path)
return fnamemodify(path, ':h')
endif
let path = findfile(marker, expand('%:p:h') . ';')
if !empty(path)
return fnamemodify(path, ':h')
endif
endfor
return expand('%:p:h')
endfunction
" Auto-Wechsel bei BufEnter
autocmd BufEnter * silent! execute 'lcd ' . fnameescape(<SID>FindProjectRoot())Diese Funktion sucht ausgehend vom Verzeichnis der aktuellen Datei aufwärts nach einer der Marker-Dateien — .git, go.mod, package.json, Cargo.toml, pyproject.toml. Sobald gefunden, wird ihr Verzeichnis als Projekt-Root verwendet und per :lcd als window-lokales Working Directory gesetzt.
Mit :lcd (statt :cd) bleibt das pro Fenster, sodass mehrere Splits parallel in unterschiedlichen Projekten arbeiten können.
Plugin: vim-rooter
Wer keine eigene Funktion schreiben will, nutzt das Plugin airblade/vim-rooter. Es macht genau das Gleiche — mit konfigurierbaren Markern und sauber gewartet:
" vim-rooter installieren (mit vim-plug oder Manager der Wahl)
Plug 'airblade/vim-rooter'
" Marker für Projekt-Roots
let g:rooter_patterns = ['.git', 'go.mod', 'package.json', 'Cargo.toml']
" Statt globalem :cd: window-lokales :lcd
let g:rooter_cd_cmd = 'lcd'
" Stille (kein Echo bei Wechsel)
let g:rooter_silent_chdir = 1vim-rooter ist die pragmatische Default-Wahl, wenn man eine zuverlässige Projekt-Root-Detection ohne eigene Vimscript-Implementierung möchte. Drei Zeilen Konfig, fertig.
Working Directory in der Statusline
Eine kleine Komfort-Verbesserung: das aktuelle Working Directory in der Statusline anzeigen — damit ist die Antwort auf „wo bin ich gerade?" immer sichtbar, ohne :pwd zu tippen.
" Default-Statusline um WD ergänzen
" %{getcwd()} expandiert zur Laufzeit das aktuelle Working Directory
set statusline=%f\ %m%r%h%w\ \|\ %{getcwd()}\ \|\ %=%l/%L,%c
" Statusline immer anzeigen (Default ist „nur bei mehreren Fenstern")
set laststatus=2Mit einem Statusline-Plugin wie vim-airline oder lightline ist diese Information meist schon Default — siehe Kapitel UI und Statusline.
Praxis-Workflows
Drei typische Anwendungs-Szenarien:
Workflow 1: zwei Projekte parallel in einer Vim-Session
" Tab 1: Projekt A
:tcd ~/projects/site
:e index.html
" Tab 2: Projekt B — eigenes WD
:tabnew
:tcd ~/projects/api
:e main.go
" Wechsel zwischen Tabs (gt/gT) — jeder Tab hat sein eigenes WD.
" :e bar.txt in Tab 1 öffnet ~/projects/site/bar.txt
" :e bar.txt in Tab 2 öffnet ~/projects/api/bar.txtWorkflow 2: kurzer Sprung in ein Konfig-Verzeichnis
" In einem Code-Tab arbeitend, kurz die Konfig editieren:
:vsplit
:lcd ~/.config/vim/
:e vimrc
" Der ursprüngliche Split bleibt im Projekt-WD,
" nur dieses Fenster ist in ~/.config/vim/.
" Nach Schließen des Splits ist man wieder im Projekt.Workflow 3: Shell-Befehl im Projekt-Root, egal welche Datei aktiv
Mit Projekt-Root-Detection (vim-rooter oder eigene Implementierung) ist :!command automatisch im Projekt-Root — egal in welchem Unterverzeichnis die aktive Datei liegt.
" Aktive Datei: src/auth/handler.go
" WD (per vim-rooter): /home/user/projects/myapp (Projekt-Root)
:!go test ./... " läuft im Projekt-Root, testet alle Pakete
:!git status " zeigt Status für das ganze Projekt
:!make build " ruft das Top-Level-Makefile aufOhne Root-Detection (oder mit autochdir) müsste man manuell ins Projekt-Root cd-en — was den Workflow regelmäßig unterbricht.
getcwd() und Vimscript-Zugriff
Für eigene Mappings und Funktionen ist getcwd() der Vimscript-Zugriff aufs Working Directory:
| Aufruf | Ergebnis |
|---|---|
getcwd() | aktuelles wirksames WD |
getcwd(-1, -1) | globales WD (ignoriert lcd/tcd) |
getcwd(0) | window-lokales WD (oder leer, falls keins gesetzt) |
getcwd(0, 0) | tab-lokales WD (oder leer) |
haslocaldir() | 1 wenn aktuelles Fenster ein lokales WD hat, 0 sonst |
haslocaldir(0, 0) | 2 wenn tab-lokales WD gesetzt ist |
Praktisches Beispiel — eine Mapping, das den Buffer-Inhalt mit dem Working-Directory-Pfad als Header oben einfügt:
" <leader>ip = insert path (Projekt-Root als Kommentar)
nnoremap <leader>ip :put = '// Project root: ' . getcwd()<CR>FAQ
Was ist der Unterschied zwischen `:cd` und `:lcd`?
:cd ändert das globale Working Directory — alle Fenster und Tabs sind danach im neuen Verzeichnis (sofern sie kein eigenes lokales gesetzt haben). :lcd ändert nur das aktuelle Fenster — andere Fenster bleiben, wo sie waren. Für gemischte Setups ist :lcd fast immer die saubere Wahl.
`autochdir` und Projekt-Root-Detection schließen sich aus
autochdir wechselt ins Datei-Verzeichnis, vim-rooter ins Projekt-Root. Beide gleichzeitig aktiv führt zu Konflikten — meistens „wer-zuletzt-kam-mahlt-zuerst". Entscheide dich für eines.
`:pwd` zeigt das wirksame WD aus aktuellem Kontext
Wenn ein window-lokales WD gesetzt ist, zeigt :pwd dieses — nicht das globale. Wer prüfen will, welche Ebene gerade greift, nutzt getcwd(0) (window) und haslocaldir() (1 = window-lokal, 2 = tab-lokal, 0 = global).
Backticks im Pfad für dynamische Werte
`:cd ``git rev-parse --show-toplevel``` wechselt ins Git-Root des aktuellen Verzeichnisses — eine schnelle, plugin-freie Variante der Projekt-Root-Detection. Geht aber nur, wenn die aktuelle Datei in einem Git-Repo liegt.
Tab-lokales WD überlebt Split-Operationen
Mit :tcd gesetzte tab-lokale Verzeichnisse gelten für alle Fenster im Tab — auch für solche, die später per :split/:vsplit hinzukommen. Wer einen Tab als eigenen Workspace betrachtet, sollte das WD per :tcd setzen, nicht per :lcd.
`:cd -` ist „back to where you came from"
Analog zur Shell wechselt :cd - zurück zum vorherigen Working Directory. Praktisch beim kurzen Abstecher in ein Konfig-Verzeichnis und schneller Rückkehr.
Weiterführende Ressourcen
Externe Quellen
- Vim Help: :cd — der globale
:cd-Befehl. - Vim Help: :lcd — window-lokale Variante.
- Vim Help: :tcd — tab-lokale Variante.
- Vim Help: autochdir — Auto-Wechsel ins Datei-Verzeichnis.
- airblade/vim-rooter — Plugin für Projekt-Root-Detection.
- Vim Help: getcwd() — Vimscript-Zugriff aufs WD.