Ein Blick unter die Haube von Coreutils for Windows
Es gibt diesen alten Satz, der Microsoft seit zwanzig Jahren anhaftet: Steve Ballmer nannte Linux 2001 ein "Krebsgeschwür", das sich am geistigen Eigentum festsaugt. Wer das im Hinterkopf hat, dem entgeht die Ironie der Build 2026 nicht. Anfang Juni hat Microsoft in San Francisco angekündigt, dass es die klassischen Unix-Kommandozeilen-Tools - ls, cp, mv, cat, rm, find, grep - künftig nativ mit Windows ausliefert. Nicht in einer virtuellen Maschine, nicht über WSL, sondern als echte Windows-Programme. Und das Pikante daran: geschrieben sind sie in Rust, nicht in C. Die Firma, die GNU einst bekriegt hat, schickt jetzt GNU-kompatible Kerntools an Windows-Nutzer aus.
Ich habe mir das Paket angeschaut, installiert und ein bisschen im Quellcode gestochert. Denn die eigentlich spannende Frage steckt nicht in der Ankündigung, sondern darin, wie Microsoft die Namenskonflikte zwischen altbekannten DOS- und Unix-Befehlen auflöst.
Was an der Build wirklich angekündigt wurde
"Coreutils for Windows" war Teil eines grösseren Entwickler-Pushs unter dem Motto, Windows zur "trusted platform for development" zu machen. Daneben gab es WSL-Container (Linux-Container nativ über WSL starten, ohne Docker-Drittwerkzeug) und einen experimentellen "Intelligent Terminal" mit Agenten-Kontext. Wichtig für die Einordnung: Coreutils wurde als allgemein verfügbar angekündigt, während WSL-Container und der Intelligent Terminal noch im Preview-Stadium sind. Wer ganz genau hinschaut, merkt aber, dass das GitHub-Repository selbst noch ein "This project is in preview" trägt - die Lage ist also irgendwo zwischen "fertig" und "fast fertig", was bei einem Tool, das man täglich nutzt, durchaus relevant ist.
Der Code liegt offen unter microsoft/coreutils auf GitHub, lizenziert unter MIT.
Installation: ein Einzeiler
Am schnellsten geht es über WinGet:
winget install Microsoft.Coreutils
Alternativ kann man den Installer direkt von der Release-Seite des Repos ziehen. Die Tools landen unter C:\Program Files\coreutils, und dieses Verzeichnis muss im System-PATH liegen, damit die Befehle in der Shell auftauchen. Eine Voraussetzung gilt es zu beachten: PowerShell 7.4 oder neuer ist Pflicht, ältere Versionen werden nicht unterstützt.
Was steckt drin
Coreutils for Windows ist kein Eigenbau von Grund auf, sondern ein von Microsoft gepflegter Build dreier bestehender Projekte:
- uutils/coreutils - die Rust-Neuimplementierung von GNU coreutils. Dasselbe Projekt steckt übrigens auch in Ubuntu, das seit Version 25.10 standardmässig auf die Rust-coreutils setzt (zusammen mit sudo-rs). Microsoft greift hier also auf einen Stack zurück, der sich auf dem Linux-Desktop bereits in der Praxis bewährt hat.
- uutils/findutils - die Pendants zu
findundxargs. - Ein GNU-kompatibles
grep, von Microsoft beigesteuert.
Alles zusammengepackt in eine einzige ausführbare Datei. Und genau hier wird es technisch interessant.
Eine kurze Randnotiz zur Lizenz, ohne ein grosses Fass aufzumachen: uutils steht unter der permissiven MIT-Lizenz, die originalen GNU coreutils unter der GPL. Für Microsoft ist das Vorhaben damit lizenzrechtlich unkompliziert, MIT erlaubt diese Art von Einbettung problemlos.
Der technische Kern: ein Multi-Call-Binary
Jetzt zum spannenden Teil. Microsoft hat nicht etwa für jeden Befehl eine eigene .exe gebaut. Stattdessen gibt es genau eine exe-Datei - coreutils.exe - die sämtliche Funktionen aller Tools enthält. Wer schon mal mit BusyBox auf einem eingebetteten System oder in einem schlanken Container gearbeitet hat, kennt das Prinzip: ein einziges Binary, das sich je nach Aufrufnamen wie ein anderes Programm verhält.
Damit das funktioniert, legt der Installer für jeden unterstützten Befehl einen NTFS-Hardlink an: ls.exe, cp.exe, cat.exe, rm.exe und so weiter zeigen physisch alle auf dieselbe coreutils.exe. Ruft man nun ls auf, startet Windows in Wahrheit coreutils.exe, das anhand seines eigenen Aufrufnamens, also argv[0] - entscheidet, welches Werkzeug es gerade sein soll, und dorthin dispatcht.
Das ist elegant: Statt zwanzig Megabyte mal zwanzig Befehle liegt der Code nur einmal auf der Platte, und die Hardlinks kosten praktisch nichts. Man kann die Verknüpfung auch selbst sichtbar machen, indem man sich die Datei-Informationen anschaut - alle Namen verweisen auf denselben Inode beziehungsweise dieselbe zugrundeliegende Datei.
Die Namenskonflikte: drei Kategorien
So weit, so sauber. Aber sobald man Unix-Werkzeuge auf ein System wirft, das seit DOS-Zeiten eigene Kommandozeilen-Tools mitbringt, sind Namenskollisionen programmiert. Hier ist der Punkt, an dem viele Erklärungen ungenau werden. Im Grunde gibt es drei Arten von Konflikten, und nur eine davon ist wirklich kniffelig.
Kategorie 1: gleicher Name, ähnliche Aufgabe - die Shell entscheidet. Das betrifft den Grossteil der Fälle: ls, cat, cp, mv, rm, pwd, tee und Konsorten. Das eigentliche Problem liegt hier nicht bei DOS, sondern bei PowerShell. Dort ist ls seit jeher ein Alias für Get-ChildItem, cat einer für Get-Content. Welche Variante läuft, hängt von drei Dingen ab: der verwendeten Shell, der Reihenfolge der Verzeichnisse im PATH und - bei PowerShell - der Alias-Tabelle. In CMD (Eingabeaufforderung) läuft die coreutils-Version meist anstandslos, in PowerShell gewinnt der Alias. Microsoft markiert diese Fälle darum mit einem Warnsymbol: ausgeliefert ja, aber ob sie greifen, musst du selbst sicherstellen, etwa mit dem vollen Namen ls.exe oder einer angepassten Alias-Tabelle.
Kategorie 2: gleicher Name, völlig andere Aufgabe - hier wird es ernst. Das sind genau zwei Befehle: find und sort. Beide gibt es unter DOS schon lange, aber sie tun etwas anderes als ihre Unix-Namensvettern. Bei find sogar etwas radikal anderes. Genau diese beiden bekommen Microsofts eigentliche Lösung verpasst, den Shim. Dazu gleich im Detail.
Kategorie 3: zu konfliktreich, gar nicht erst ausgeliefert. Manche Namen kollidieren so hart mit fest verdrahteten Windows-Built-ins, dass Microsoft die coreutils-Variante schlicht weglässt: dir, more, paste, expand und whoami. Dazu kommen kill und timeout, die mangels POSIX-Signalen unter Windows ohnehin keinen Sinn ergeben.
Nach meinen Test konnte ich nun folgendes feststellen:
Läuft in CMD und PowerShell find und sort (beide als integrierter Port des DOS-Befehls) sowie hostname (eine Obermenge des Windows-Built-ins). Diese drei sind die einzigen Konfliktbefehle, die in beiden Shells sauber durchlaufen.
Läuft in CMD, kollidiert in PowerShell cat, cp, ls, mv, pwd, rm, sleep, tee, uptime. In CMD problemlos. In PowerShell überdeckt der gleichnamige Alias die coreutils-Variante (etwa ls für Get-ChildItem oder cat für Get-Content). Mit dem vollen Namen wie ls.exe kann man dennoch die coreutils Version erzwingen.
Konflikt in beiden Shells date, echo, mkdir, rmdir. Werden mitgeliefert, kollidieren aber je nach Kontext mit einem Built-in.
Gar nicht ausgeliefert dir, expand, more, paste und whoami kollidieren zu hart mit fest verdrahteten Windows-Befehlen. kill und timeout fehlen, weil es unter Windows keine POSIX-Signale gibt (timeout hängt an der Funktionalität von kill).
Auffällig ist die erste Gruppe: find und sort laufen in beiden Shells sauber, obwohl sie eigentlich mit gleichnamigen DOS-Befehlen kollidieren müssten. Wie schaffen die das? Damit sind wir beim spannendsten Teil.
Jeder Befehl, der hier nicht auftaucht, ist trotzdem dabei, die Auflistung listet nur die Konfliktfälle. Schön zu sehen: In CMD läuft fast alles, während PowerShell wegen seiner Alias-Tabelle deutlich mehr Reibung erzeugt. Und genau eine Sache fällt auf: find und sort sind die einzigen Konfliktbefehle die in beiden Shells sauber laufen. Wie schafft Microsoft das, wo sie doch mit bestehenden DOS-Befehlen kollidieren?
Die elegante Lösung am Beispiel find
find ist das Paradebeispiel, weil die beiden Welten hier maximal weit auseinanderliegen. Das DOS-find durchsucht den Inhalt von Dateien nach einer Textzeichenkette - im Grunde ein primitiver grep-Verschnitt, gesteuert über /switch-Syntax wie /V (Zeilen ohne Treffer), /C (nur zählen), /N (mit Zeilennummern) oder /I (Gross- und Kleinschreibung ignorieren). Das Unix-find dagegen durchwandert den Verzeichnisbaum und filtert nach Namen, Typ, Grösse oder Änderungsdatum, mit Prädikaten wie -name, -type oder -mtime. Gleicher Name, zwei vollkommen verschiedene Programme.
Microsoft löst das nicht, indem ein Tool das andere verdrängt, sondern mit einem Shim das ist eine dünne Vermittlungsschicht. Statt der reinen uutils-Variante liefert das Paket für find (und ebenso für sort) eine Erkennungslogik mit, die jeden Aufruf in eine von drei Kategorien einsortiert: Dos, Gnu oder Ambiguous. Erst danach steht fest, welche Implementierung tatsächlich startet. Wichtig: Geprüft wird nicht der Befehlsname (der ist ja immer find), sondern die Form der Argumente.
Spannend ist, wie find das macht. Die Erkennungsfunktion liest nicht das fertig geparste Argument-Array, sondern die rohe Kommandozeile über GetCommandLineW(). Das ist Absicht: Sie muss erkennen, ob das erste Argument in Anführungszeichen stand und ob ein \"-Escape vorkommt beides sind typische Unix-Eigenheiten, die im geparsten argv schon verloren gegangen wären. Dann arbeitet sie sich durch die Tokens:
- Beginnt das erste Token mit
/oder ist es ein bekannter DOS-Switch (/C,/I,/N,/OFF,/OFFLINE,/V), ist die Sache klar: DOS Variante von find wird aufgerufen. - Ist das erste Token nicht gequotet, gilt es als GNU Variante. Da das DOS-
findseinen Suchstring zwingend in Anführungszeichen verlangt. - Ist es gequotet, danach folgt aber ein echtes GNU-Prädikat (
-name,-type,-printund so weiter): ebenfalls GNU. Folgen stattdessen nur Tokens ohne-, also schlicht Dateinamen, dann DOS. - Lässt sich beides nicht entscheiden, bleibt der Aufruf Ambiguous.
Und genau für diesen unentschiedenen Fall steckt der eigentliche Clou im Code: ein systemweiter Standardwert in der Registry. Unter HKLM\SOFTWARE\Microsoft\coreutils entscheidet der DWORD-Wert DefaultFind, was bei Mehrdeutigkeit gewinnt - 0 oder fehlend bedeutet DOS, 1 bedeutet GNU. Wer also grundsätzlich lieber das Unix-find als Default hätte, setzt diesen Wert auf 1, und ein blosses find "*.md" ohne weitere Argumente landet künftig beim uutils-find statt beim DOS-Pendant.
Ein paar Beispiele, wie das in der Praxis greift:
find /N "TODO" notes.txt- erstes Token ist ein DOS-Switch, also DOS-find. Ein altes CMD-Skript läuft unverändert weiter.find . -name "*.md"- erstes Token nicht gequotet, also Unix-find. Genau wie unter Linux.find "hello" log.txt- gequoteter String, danach nur ein Dateiname ohne-: DOS-finddurchsuchtlog.txtnach "hello".find "*.md"mehrdeutig, hier entscheidet der Registry EintragDefaultFind.
Der Code für den Shim dazu findet man hier: https://github.com/microsoft/coreutils/blob/main/src/nthelpers.rs
Genau deshalb stehen find und sort oben in der ersten Gruppe, die in beiden Shells sauber läuft: Sie kollidieren nicht mit der Shell, weil der Shim die Kollision intern auflöst, bevor sie überhaupt entsteht.
Bei sort ist die Erkennung deutlich simpler, weil die Aufgabe ähnlicher ist, beide Varianten sortieren Textzeilen. Hier genügt ein Blick auf das erste Argument: Beginnt es mit / (etwa /R für umgekehrte Reihenfolge), ist es DOS. Beginnt es mit - (-r, -n, -k) oder mit + gefolgt von einer Ziffer (die alte GNU-Feldsyntax), ist es GNU. Sonst greift wieder ein Registry-Default, diesmal DefaultSort. Zwei Befehle, dasselbe Grundprinzip: erst die Argumente klassifizieren, im Zweifel die Registry fragen.
Die Stolpersteine
Wer die Microsoft Coreutils jetzt installiert und loslegt, sollte neben den Namenskonflikten von oben noch ein paar weitere Eigenheiten kennen, sonst gibt es Überraschungen.
Windows-Eigenheiten
Ein paar Dinge funktionieren unter Windows naturgemäss anders.
- CRLF statt LF: Windows-Textdateien nutzen
\r\nfür einen Zeilenumbruch. Die meisten Tools gehen damit transparent um, aber bei Pattern-Matching mit$oder exakten Byte-Zählungen kann das Resultat abweichen. - Kein
/dev/null: Stattdessen nutzt manNUL, etwafind . -name "*.log" > NUL. - Keine POSIX-Signale:
SIGHUP,SIGPIPEund Konsorten gibt es nicht.Ctrl+CalsSIGINTfunktioniert aber wie erwartet. - Pfadtrenner: Sowohl
/als auch\werden akzeptiert. Manche Tools geben\-getrennte Pfade aus, was beim Weiterpipen stören kann. - Rechte über ACLs statt POSIX-Bits: Prädikate wie
find -permverhalten sich entsprechend anders oder fehlen. - Symlinks: Bestehende lesen geht ohne Weiteres, neue anlegen braucht den Entwicklermodus oder ein Terminal mit Adminrechten.
Was bewusst fehlt
Nicht alles aus dem uutils-Fundus ist dabei. Manche Befehle wurden absichtlich weggelassen, weil sie auf reine POSIX-Konzepte setzen oder bestehende Windows-Skripte zerschiessen würden: dd, dircolors, shred, sync, uname sind schlicht nicht nützlich unter Windows, und die ganze Riege der Rechte- und Nutzer-Tools wie chmod, chown, chroot, id, mkfifo, tty, who und users, die ohne POSIX-Unterbau keinen Sinn ergeben.
In der Praxis
Genug Theorie, ein paar Beispiele aus dem laufenden Betrieb. In einem CMD-Fenster mit korrektem PATH klappt das meiste direkt:
# Verzeichnis im Unix-Stil auflisten
ls -la
# Rekursiv nach Markdown-Dateien suchen (Unix-find)
find . -name "*.md"
# Mit grep durch eine Logdatei filtern
cat server.log | grep "ERROR"
# Und der DOS-find lebt weiter
find /N "TODO" notes.txt

In PowerShell muss man hingegen aufpassen: Tippt man ls, bekommt man weiterhin Get-ChildItem, nicht das coreutils-ls. Wer die neue Variante erzwingen will, ruft sie mit vollem Namen auf, etwa ls.exe, oder passt seine Alias-Tabelle an. Das ist genau die Art von Detail, die man einmal verstanden haben muss, danach ist es trivial.

Einordnung: mehr als ein Komfort-Feature
Auf der Oberfläche ist Coreutils for Windows eine Bequemlichkeit für Leute wie mich, die ständig zwischen Windows, WSL, macOS und Linux pendeln. Die gleichen Befehle, die gleichen Flags, die gleichen Pipelines, das spart täglich kleine Reibungsverluste.
Unter der Oberfläche aber erzählt das Projekt eine grössere Geschichte. Zum einen technisch: Die Rust-Reimplementierung ist robust genug, dass gleich zwei Schwergewichte sie in ihr System holen. Ubuntu seit 25.10 als Standard, und jetzt Microsoft für Windows. Das ist ein dickes Ausrufezeichen für das uutils-Projekt und für den "Rust-Systemwerkzeug-Bereich" generell. Innerhalb kurzer Zeit ist aus einem ambitionierten Community-Projekt ein Stück Infrastruktur geworden, das auf zwei der wichtigsten Plattformen läuft.
Zum anderen symbolisch: Microsoft hört auf, von Entwicklern zu verlangen, sich zwischen Windows und Linux zu entscheiden. Stattdessen holt es sich die Linux-Welt ins eigene Haus. Ob man das als Umarmung oder als Vereinnahmung liest, bleibt jedem selbst überlassen.
Fazit
"Coreutils for Windows ist ein kleines Tool mit einer grossen Hintergrundgeschichte. Die Installation ist ein Einzeiler, die Architektur mit dem Multi-Call-Binary und den NTFS-Hardlinks ist sauber durchdacht, und die Lösung des find-Konflikts über einen Shim, der DOS- und Unix-Syntax gleichzeitig bedient, ist genau die Art pragmatischer Eleganz, die man selten zu sehen bekommt. Wer viel zwischen Systemen wechselt, sollte es ausprobieren. Und wer sich für das grosse Bild interessiert, sollte im Hinterkopf behalten, dass hier gerade ein Stück GNU-Geschichte umgeschrieben wird.
In Rust. Unter MIT. Von Microsoft.