Freeware Windows Command Line Utilities: The Collection and Its Switches
Nine small programs, each doing one thing that the shell of the day could not. Read as a set, this collection of freeware Windows command line utilities is a fairly precise inventory of what was missing from Windows NT, and since most of the gaps have since been filled, it also works as a map from an old switch to the command that replaced it.
The freeware Windows command line utilities in this collection
LS-Tools was published as a set of freeware Windows command line utilities for Windows NT between roughly 1997 and 2002, growing one utility at a time. There was no installer and no framework: each member was a single console executable that printed its own usage when run with a help switch. That format is the reason the documentation survives so well: the entire interface of each tool fits in a screen.
The selection has an obvious logic once you group it by what it reaches for. Two utilities touch power and sessions, two touch identity and access, three are pipeline plumbing, one manipulates the file system in a way the shell refused to expose, and one is a build helper. Nothing in the set overlaps with anything else in it.
Members and their replacements
| Utility | Did | Replaced by |
|---|---|---|
ln.exe | create a hardlink on NTFS | mklink /H, fsutil hardlink |
down.exe | shutdown, reboot, logoff, suspend, hibernate, local or remote | shutdown, powercfg |
apm.exe | battery status; suspend or hibernate | powercfg, Get-CimInstance Win32_Battery |
errortext.exe | translate a Win32 error number to text | net helpmsg, certutil -error |
clipdump.exe | print the clipboard to standard output | Get-Clipboard (no built-in console equivalent) |
copytosystem.exe | copy files to the system directory on both 9x and NT | nothing. The two-family problem it solved is gone |
scacl.exe | view and edit service and driver access lists | sc sdshow, sc sdset |
who.exe | print the current account, groups and privileges | whoami /all |
setenv.exe | set an environment variable in another process | nothing supported: use for /f in the other direction |
Hardlinks, before the shell would admit they existed
NTFS has supported hardlinks since its first release, and for years no shipped command
exposed them. ln.exe did, in one line, with the deliberately Unix-shaped name
that told you what it was.
USAGE: ln FILE LINK
FILE name of an existing file (the link destination)
LINK name of the link to create. An existing file of this name is overwritten.
Note: NTFS supports hardlinks to files only.
The overwrite behaviour in that note is worth keeping in mind if you ever run across a
script that uses it. There is no confirmation and no dry run. The current equivalents are
safer by default: mklink /H refuses to overwrite, and
fsutil hardlink list will tell you how many names a file already has, which
is the question that matters before deleting one.
Identity, error codes and the clipboard: the pipeline switches
Three of the utilities exist because a shell script needed a value that the shell could
not produce. who.exe answered "who am I, what am I in, and what may I do",
with separate switches for the account, the group list, the group list with identifiers,
the privileges, and the privileges with explanations.
USAGE: Who.exe [/u /g /G /p /P /a /A /n]
/u account information /p privileges
/g group memberships /P privileges with explanations
/G group memberships with SIDs /a everything (= /u /g /p)
/n suppress the version banner /A everything, verbose (= /u /G /P)
That is whoami /all now, and the substitution is exact. The one habit worth
carrying over is the banner switch: a utility that prints its own name and version above
its output breaks any script that parses the first line, and the tools of this period
almost all offered a way to suppress it.
errortext.exe turned 0x5 into "Access is denied", which sounds
trivial until you have read a log full of bare numbers. clipdump.exe printed
the clipboard to standard output, and remains the most quietly useful of the nine, because
the built-in clip only ever writes. A batch file that needs to read the
clipboard still has to call out to PowerShell.
The environment variable trick, and why nothing replaced it
setenv.exe set an environment variable in the parent process, or in any
process named by identifier, and could take the value from standard input. Which meant a
batch file could capture the output of any command into a variable. Something the shell
of the time simply could not do.
USAGE: setenv [/stdin /stderr] [/P[=] <pid>] <varname[=]> <value>
/P <pid> target process, decimal or 0x-prefixed. Omitted or zero means the parent.
/stdin take input from the command line while it lasts, then from standard input
Examples:
time /T | setenv /stdin TIME1 ← pipe the current time into TIME1
setenv /P 4711 INCLUDE C:\Inc ← set INCLUDE for process 4711
No supported command does this, and none should: a process owns its environment block, and
writing into another one means writing into another process's memory. The gap it filled
closed from the other direction instead. for /f captures command output into a
shell variable, and in PowerShell assignment does it natively. The lesson generalises. When an old utility has no modern equivalent, the usual reason is that the problem was
solved somewhere else rather than that the capability was lost.
Power, sessions and the two that need their own page
apm.exe reported battery state and could suspend or hibernate the machine
through the pre-ACPI power interface; its later version also printed remaining time when
the hardware supplied it. Everything it did is inside powercfg now, along
with the diagnostics that answer why a machine will not stay asleep.
The two heavyweights in the collection have enough surface to be treated separately.
down.exe reached power states on remote machines that the documented interface
does not expose, by copying itself to the target and running there. The mechanism and its
consequences are on the remote shutdown page.
scacl.exe edited the access list on services and drivers, with the letter
notation that is still the clearest way to read a service descriptor; that is on the
service permissions page.
Read together, these freeware Windows command line utilities also show where the
substitution stops. Seven of the nine map onto a built-in command, and two do not:
copytosystem.exe solved a problem that no longer exists, one binary for both
the 9x and the NT family, and setenv.exe has no supported equivalent at all.
Writing into another process's environment is not something current Windows offers, so a
script that depended on it has to be turned around to read a value out of the child
instead of pushing one in.
Questions about these Windows command line utilities
Do hardlinks really work on Windows?
Yes, on NTFS, and they have since Windows NT. A hardlink is a second directory entry pointing at the same file record, so both names are equally real and the data survives until the last one is deleted. The restriction is that both names must be on the same volume and the target must be a file. NTFS has no hardlinks to directories, which is what junctions are for.
What is the modern equivalent of a hardlink tool?
mklink /H link target for a hardlink and mklink /J for a directory junction, both built in since Windows Vista. fsutil hardlink create does the same job and additionally lists existing links to a file, which mklink cannot.
Can a program set an environment variable in its parent shell?
Not through the documented interface. A process owns its own environment block; a child cannot alter the parent. Tools that appeared to do it wrote into the parent process memory directly, which worked and is not supportable. The standard route is the reverse: the child prints the value and the shell captures it, which is what for /f and command substitution exist for.
Why does clip only work one way?
The built-in clip writes the clipboard from standard input and has no read direction at all. Reading it from a batch file needs PowerShell, via Get-Clipboard, or a helper. That asymmetry is why a small clipboard-to-stdout utility was worth carrying for years.
How do I translate an error number like 0x5 into text?
net helpmsg 5 for a decimal system error, and certutil -error 0x80070005 for a hexadecimal one including COM results. Both read the same message tables a dedicated translator would, so the utility that used to do this has a built-in replacement on any current machine.
Is APM power management still relevant?
No. The interface these tools used predates ACPI, and everything since Windows 2000 uses the newer model. powercfg covers the same ground and much more, including which devices are permitted to wake the machine, which is the question that actually matters when a suspend does not stick.
What does who.exe do that whoami does not?
Nothing today. whoami /all prints the account, the group memberships with their identifiers and the privileges with descriptions, which is the full output the older tool assembled from three separate switches. Before whoami shipped in the box, that information needed a utility.
Are 32-bit tools from that era still runnable?
A 32-bit console binary generally still runs on 64-bit Windows through the compatibility subsystem. Whether it does anything useful is a different question: several of these called interfaces that have since been removed or replaced, and one of them ran as a service to do its work, which modern security defaults make awkward. Treat them as documentation rather than as software.
Which of these needed administrator rights?
The remote shutdown helper needed them on the target because it installed a service there. Changing a service access list needs them on the object being changed. The rest, hardlinks aside (those need write access to the directory), ran as an ordinary user, which was part of the point of the collection.
Where did the MFC dependency come from?
Two runtime archives shipped alongside the collection because some of the graphical components linked against the Microsoft Foundation Classes library dynamically rather than statically. The console utilities did not need it. A missing runtime library producing an immediate startup failure was a routine annoyance of the period and is the reason those archives existed.