If you work across Windows and Linux, you know the friction. A script runs fine on the server, you pull it onto your Windows laptop, and it immediately wants ls, grep, find, cp -r — none of which Windows ships. So you reach for WSL, or Git Bash, or you rewrite the thing in PowerShell.
Microsoft just made that less necessary. It released an official package, Coreutils for Windows, that puts the familiar Unix commands natively on Windows — no compatibility layer, no Linux VM. The genuinely interesting part is what it’s built on: not a fresh Microsoft rewrite, but a Rust reimplementation of GNU coreutils that’s already been making its way into the Linux world. Here’s the story, and how to actually use it.
It’s one winget package:
winget install Microsoft.CoreutilsThat gives you native ls, cp, mv, rm, cat, grep, find, xargs, sort, date, pwd, and most of the rest of the coreutils family, running directly on Windows. They support the standard Unix flags (ls -la, cp -r) and the usual --help. It works in CMD, in PowerShell 7.4 and later, and in Windows Terminal. The project is in preview, so expect some rough edges, but the everyday file-and-text commands are there.
It’s built on a Rust rewrite of coreutils
Microsoft didn’t write all of this from scratch. The package is a Microsoft-maintained build of uutils — specifically uutils/coreutils, plus uutils/findutils (which brings find and xargs) and a uutils grep. uutils is a cross-platform reimplementation of the classic GNU coreutils, written in Rust, and it’s mature enough that Linux distributions have already started trialing and adopting it in place of the decades-old C versions. It’s MIT-licensed and has spent years chasing close compatibility with GNU’s own test suite, which is what makes it a credible drop-in rather than a toy.
Rust is the through-line here, and it buys two things. First, memory safety by default, which removes a whole category of bugs and vulnerabilities that the original C tools have to guard against by hand. Second — and this is why it works on Windows at all — the same codebase compiles for Linux, macOS, and Windows. The hard part Microsoft took on wasn’t reimplementing ls; it was the Windows-specific plumbing: NTFS, long paths, UTF-16 file names, and access-control lists. The command logic came for free from an existing open-source project.
That’s the quiet significance of this release. A Rust rewrite of the Unix userland is going mainstream on all three major platforms at once — and Microsoft shipping it on Windows is a big vote of confidence in that direction.
The multi-call binary trick
Here’s a neat implementation detail. Coreutils isn’t dozens of separate .exe files. It’s a single binary — coreutils.exe — that contains every command, and the install creates small per-command names that all point back to it:
ls.exe ─┐
cp.exe ─┤
rm.exe ─┼──▶ coreutils.exe (one binary)
cat.exe ─┤
grep.exe─┘When you type ls, you’re really running coreutils.exe, which looks at the name it was invoked under — argv[0] — and dispatches to its built-in ls. So these two are equivalent:
coreutils.exe ls -la # explicit
ls.exe -la # same binary, dispatched by the name "ls"If you’ve used BusyBox on an embedded Linux system, this is the exact same idea. The payoff is practical: one binary to update instead of fifty, atomically, and a much smaller install than shipping every tool separately.
Using it: the PATH and PowerShell gotcha
Installing it is the easy part; getting the right ls to run is where people trip. Windows already has some of these names — a built-in sort and a find that are completely different DOS-era tools — and PowerShell aliases ls, cp, and rm to its own cmdlets (Get-ChildItem, Copy-Item, Remove-Item).
So whether you actually get the coreutils version depends on three things: which shell you’re in, the order of entries in your PATH, and — in PowerShell — the alias table. If a built-in wins, you’ll get behavior you didn’t expect.
The fix is to check what’s really running before you rely on it. In PowerShell:
PS> Get-Command ls
# CommandType Name Definition
# Alias ls Get-ChildItem <-- PowerShell's alias is winningIf an alias is intercepting it, you have a few options: call it explicitly as coreutils ls, use the full path to ls.exe, or remove the PowerShell alias (Remove-Item Alias:ls) so the real binary wins. In CMD, where ls shows you which one is first on the PATH. The general rule: in a fresh setup, confirm with Get-Command / where rather than assuming.
Making it the default
If you want a bare ls to mean the coreutils version in your shell, you have to get past PowerShell’s built-in aliases, which take priority over anything on your PATH. The cleanest way is to remove the ones you want to override in your PowerShell profile, so the real binaries win:
# in $PROFILE
Remove-Alias ls, cp, rm, mv, cat -ErrorAction SilentlyContinueNow ls resolves to ls.exe from the coreutils install. If you’d rather not disturb the built-ins, do the opposite and stay explicit: keep PowerShell’s ls, and reach for the Unix one as coreutils ls only when you specifically want GNU behavior. In CMD there are no aliases to fight — just PATH order, so put the coreutils directory early if you want it to win. Either way, the habit that saves you grief is Get-Command <name>: it tells you exactly which binary or alias a name resolves to before a script quietly depends on the wrong one.
Try it: the commands you already know
Once it’s installed and resolving, the point is that nothing is surprising — your muscle memory just works:
ls -la # long listing, including hidden files
grep -rn "TODO" . # recursive search with line numbers
find . -name "*.log" -type f # findutils' find, not the DOS one
cp -r src/ backup/ # recursive copy
cat a.txt b.txt | sort -u # pipelines work end to endSame flags, same pipeline behavior you’d get on Linux, because it’s the same code underneath. The win isn’t any single command — it’s that a script full of them can move between your Windows machine and a Linux server without a translation step.
How much is actually there?
The README frames inclusion generously: any command it doesn’t explicitly call out is in the box. The exclusions are the short, principled list below; everything else from coreutils — plus findutils’ find and xargs, and a grep — comes along. So beyond the headline ls/cp/rm, you also get the text-wrangling staples (head, tail, wc, cut, tr, sort, uniq, tee) and the path-and-utility commands (basename, dirname, realpath, seq, env). In practice that covers the overwhelming majority of what a cross-platform shell script ever reaches for.
What’s not included — and why
Coreutils for Windows doesn’t ship every GNU tool, and the omissions are principled rather than lazy. Some Unix concepts simply don’t map onto Windows, so those commands were intentionally left out:
| Command(s) | Why it’s not included | What to reach for instead |
|---|---|---|
chmod, chown, chgrp, id, groups, install, stty, who | POSIX-only — Windows uses ACLs, not POSIX permission bits, so these have no faithful equivalent | Windows icacls, or WSL |
kill, timeout | rely on Unix signals, which Windows doesn’t have | PowerShell Stop-Process; WSL for real signals |
dd, dircolors, shred, sync, uname | judged not particularly useful on Windows | PowerShell equivalents, or WSL |
The permissions group is the most fundamental. Linux file permissions are POSIX bits (rwxr-xr-x); Windows security is built on access-control lists, a different model entirely. There’s no honest way to make chmod 644 mean something on NTFS, so rather than fake it, those tools are dropped. Same logic for kill and timeout: without Unix signals there’s nothing for them to send.
This is the line where the package stops and WSL begins. If your script genuinely needs chmod, signals, or other deep POSIX behavior, a real Linux environment is still the answer.
Where it fits next to WSL and Git Bash
It helps to be clear about what this replaces and what it doesn’t.
For the everyday case — ls -la | grep, cp -r, a cross-platform build script that just needs standard file and text commands — Coreutils for Windows is lighter than the alternatives. There’s no Linux VM to boot like WSL, and no separate Unix environment bolted on like Git Bash; the commands run as native Windows executables, talking directly to the OS. That means less memory overhead and fewer moving parts, and it makes single-file scripts genuinely portable without the usual “on Windows you’ll need to…” caveats.
For the hard case — anything that leans on POSIX permissions, signals, /proc, a full Linux filesystem, or Linux-only binaries — WSL remains the right tool, and nothing here changes that. The two are complementary: native coreutils for the 90% of cross-platform scripting that’s just file and text wrangling, WSL for the genuinely Linux-shaped work.
One more honest caveat worth keeping in mind: because it’s native, it inherits Windows’ filesystem semantics. Symbolic links work differently (NTFS reparse points versus Linux inodes), and line endings still differ between platforms, so a few text operations can surprise you when files come from mixed sources. It’s preview software bridging two genuinely different operating systems — most things just work, but it isn’t magic.
Why this is a bigger deal than “Windows got ls”
Step back and the headline isn’t really “Windows has ls now.” It’s that a memory-safe, single-codebase reimplementation of the Unix userland is becoming the common substrate across Linux, macOS, and Windows. The same uutils code that some Linux distributions are adopting in place of GNU coreutils is now what runs when you type cp on Windows.
For anyone who writes cross-platform tooling, that convergence is the real win. The dream has always been to write a shell script once and have it behave identically everywhere. Native coreutils on Windows, built from the same source as the Linux version, gets meaningfully closer to that than any compatibility shim ever did.
Is a preview safe to lean on?
“Preview” sounds scarier than it is here. These commands read and write files through the Windows API the same way any native tool does — there’s no exotic risk to your filesystem. What preview really means is that the edges aren’t fully settled: unusual paths, mixed encodings, or less-common flags may not yet behave exactly like GNU, and the set of included commands will keep growing. For everyday ls/cp/grep/find work it’s stable enough to use now; just don’t wire it into a critical pipeline that can’t tolerate a surprise until it’s out of preview. And since the repo is open, any rough edge you hit is worth filing — that’s how the Windows-specific behavior gets sanded down.
Worth installing?
For the everyday cross-platform case — typing ls -la | grep, cp -r, running a build script that just wants standard file and text commands — yes, install it: it’s native, light, and there’s no VM to boot or Unix environment to bolt on. Just go in expecting the PATH-and-PowerShell-alias gotcha, and run Get-Command to confirm you’re getting the coreutils version rather than a built-in or an aliased cmdlet. Keep WSL for the genuinely POSIX-shaped work — anything that leans on signals (kill, timeout) or real permission bits (chmod, chown) — because coreutils deliberately leaves those out. And remember it’s still preview, so kick the tires before you wire it into anything you can’t afford to have surprise you.

Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.