Shell Translator
The same task in CMD, PowerShell and Linux bash side by side, with the gotchas: dir /s, deltree, findstr, ifconfig and 120 more.
For people who still type dir /s.
Look up a task, not a command: "delete a folder and everything in it" shows rmdir /s /q, Remove-Item -Recurse -Force and
rm -rf side by side, with the catch that bites when you switch between them. Search for anything you remember, including
old names like deltree, xcopy or ifconfig, and pick which shell you're coming from. It's all built into the page: searching sends nothing anywhere.
- PowerShell's dir, ls and cat aren't the DOS or Linux commands
- In PowerShell they're aliases for Get-ChildItem and Get-Content, so they take PowerShell parameters: dir -Recurse works, dir /s doesn't, and ls -la fails. On Linux and macOS, PowerShell 7 drops the aliases that clash with native tools, so ls there is the real ls.
- PowerShell pipes objects; CMD and bash pipe text
- Get-ChildItem | Where-Object Length -gt 1MB filters on a real number. In CMD and bash every command receives lines of text, which is why Linux leans on grep, awk and cut to pick text apart. Text tools in a PowerShell pipeline see the formatted output, not the objects.
- Linux is case-sensitive; Windows isn't
- Report.txt and report.txt are two different files on Linux, and commands, options and variables are case-sensitive too (-r and -R often mean different things). Windows file names and PowerShell cmdlets and parameters ignore case.
- find is three different things
- In CMD, find searches for text inside files. On Linux, find searches for files by name, size or date. PowerShell has no find alias, so typing find on Windows runs the CMD text-search tool find.exe.
- > writes different encodings
- Windows PowerShell 5.1 writes UTF-16 LE ("Unicode") with >, which many Linux tools and older programs can't read. PowerShell 7 writes UTF-8 without a BOM. CMD writes in the console code page. Use Out-File -Encoding utf8 or Set-Content when it matters.
- Paths: backslash vs slash, drive letters vs one tree
- Windows uses C:\Users\ann with a drive letter per volume; Linux has one tree from / with drives mounted inside it (/mnt/data). PowerShell accepts both / and \ on Windows; bash treats \ as an escape character, not a separator.
- There's no Recycle Bin on the command line
- del, Remove-Item and rm delete immediately on every platform; nothing goes to the Recycle Bin or Trash. rm -rf and Remove-Item -Recurse -Force don't ask first.
- Quoting rules differ
- CMD only understands double quotes and escapes with ^. PowerShell and bash both treat single quotes as literal and expand variables in double quotes, but PowerShell escapes with a backtick (`) and bash with a backslash.
127 tasks
Listing, copying, moving, renaming, deleting and linking files and folders.
| Task | CMD | PowerShell | Linux (bash) |
|---|---|---|---|
| List files in a folder | dir | lsGet-ChildItemAliases: gci, dir, ls | ls -l |
Gotcha: PowerShell's dir and ls are Get-ChildItem, so dir /w and ls -la fail there. Linux ls hides dot files unless you add -a. PowerShell 7 on Linux and macOS has no ls alias: ls runs the native ls. | |||
| List everything in a folder and its subfolders | dir /s | gci -rGet-ChildItem -RecurseAliases: gci, dir, ls | ls -Rfind . lists one path per line, easier to process. |
Gotcha: dir /s prints a block per folder with totals; Get-ChildItem -Recurse emits file objects you can filter and sort; ls -R groups by folder like dir /s. | |||
| List full paths only, one per line | dir /s /b | gci -r -NameGet-ChildItem -Recurse | ForEach-Object FullName | find . |
Gotcha: -Name gives paths relative to the folder; use FullName for absolute paths. find prints paths starting with ./ unless you give it an absolute start folder. | |||
| List directories only | dir /a:d | gci -DirectoryGet-ChildItem -Directory | ls -d */Or: find . -maxdepth 1 -type d |
Gotcha: dir /a:d also lists the . and .. entries. ls -d */ skips hidden folders unless dot globbing is on. | |||
| List files only (no folders) | dir /a:-d | gci -FileGet-ChildItem -File | find . -maxdepth 1 -type f |
Gotcha: The minus in /a:-d means "not". ls has no files-only switch; find -type f is the usual answer. | |||
Gotcha: On Windows "hidden" is a file attribute; on Linux a file is hidden just because its name starts with a dot. | |||
| List newest files first (or by size) | dir /o:-d/o:-s for largest first | gci | sort LastWriteTime -DescGet-ChildItem | Sort-Object LastWriteTime -Descending | ls -ltls -lS for largest first |
Gotcha: dir /o:d is oldest first and the minus reverses it; ls -t is already newest first (add -r to reverse). | |||
| Find files by name anywhere under a folder | dir /s /b *.log | gci -r -Filter *.logGet-ChildItem -Recurse -Filter *.log | find . -name '*.log' |
Gotcha: Quote the pattern in bash, or the shell expands *.log in the current folder before find sees it. find -name is case-sensitive; use -iname to ignore case. | |||
| Copy a file | copy a.txt b.txt | cp a.txt b.txtCopy-Item a.txt b.txtAliases: copy, cp, cpi | cp a.txt b.txt |
Gotcha: CMD's copy asks before overwriting when you type it (not in batch files); Copy-Item and cp overwrite silently. Use cp -i to be asked. | |||
| Copy a folder and everything in it | robocopy src dst /excopy src dst /e /i also works; robocopy is its more capable successor. | Copy-Item src dst -RecurseAliases: copy, cp, cpi | cp -r src dst |
Gotcha: If dst already exists, Copy-Item -Recurse and cp -r put the copy inside it (dst\src); robocopy copies src's contents into dst. Check with the Robocopy builder for anything bigger than a quick copy. | |||
| Move a file or folder | move a.txt D:\Archive\ | mv a.txt D:\Archive\Move-Item a.txt D:\Archive\Aliases: move, mv, mi | mv a.txt /archive/ |
Gotcha: CMD's move can't move a folder to another drive; Move-Item and mv can. mv overwrites without asking (mv -i asks). | |||
| Rename a file | ren old.txt new.txt | ren old.txt new.txtRename-Item old.txt new.txtAliases: ren, rni | mv old.txt new.txt |
Gotcha: CMD renames by wildcard (ren *.txt *.bak); Rename-Item doesn't: pipe files in with -NewName { $_.Name -replace '\.txt$','.bak' }. Linux has no rename command in coreutils; mv renames one file. | |||
| Delete a file | del file.txt | rm file.txtRemove-Item file.txtAliases: del, rm, erase, ri | rm file.txt |
Gotcha: None of these use the Recycle Bin. del *.* asks "Are you sure?"; rm *.* doesn't (and on Linux *.* only matches names containing a dot). | |||
| Delete a folder and everything in it | rmdir /s /q folderAliases: rd /s /q deltree did this in MS-DOS; it's gone since Windows NT. | rm folder -r -foRemove-Item folder -Recurse -ForceAliases: rm, rmdir, del, rd, ri | rm -rf folder |
Gotcha: All three delete immediately with no undo and, with /q, -Force or -f, without asking. -Force is needed for hidden and read-only files. Double-check the path; a stray space in rm -rf / tmp is the classic disaster. | |||
| Create a folder (with parent folders) | mkdir a\b\cAliases: md | mkdir a\b\cNew-Item -ItemType Directory a\b\cAliases: mkdir, md | mkdir -p a/b/c |
Gotcha: CMD and PowerShell create missing parent folders automatically; bash needs -p (which also stops the error when the folder exists). New-Item errors if it exists unless you add -Force. | |||
| Create an empty file (or update its time) | type nul > file.txt | ni file.txtNew-Item file.txtAliases: ni | touch file.txt |
Gotcha: touch only updates the time of an existing file. type nul > and New-Item -Force empty an existing file, so check first. To update a time in PowerShell: (Get-Item file.txt).LastWriteTime = Get-Date. | |||
| Show a file's size | dir file.iso | (gi file.iso).Length(Get-Item file.iso).Length | ls -lh file.iso |
Gotcha: Length is in bytes; divide with PowerShell's size literals, (Get-Item f).Length / 1GB. ls -h prints human-readable sizes. | |||
| View a file's attributes or permissions | attrib file.txticacls file.txt shows permissions. | (gi file.txt).Attributes(Get-Item file.txt).AttributesGet-Acl for permissions. | ls -l file.txtstat file.txt shows everything. |
Gotcha: Windows has attributes (read-only, hidden, archive, system) plus ACLs; Linux has an owner, a group and rwx bits (and optional ACLs via getfacl). | |||
| Make a file read-only | attrib +r file.txt | (gi file.txt).IsReadOnly = $true(Get-Item file.txt).IsReadOnly = $true | chmod a-w file.txt |
Gotcha: On Windows read-only is an attribute anyone with permission can clear; on Linux removing write permission is a permission change, and root can still write. | |||
| Create a symbolic link | mklink link.txt target.txtmklink /d for a folder, /j for a junction. | New-Item -ItemType SymbolicLink -Path link.txt -Target target.txt | ln -s target.txt link.txt |
Gotcha: The argument order is reversed: mklink and New-Item take the link first, ln -s takes the target first. On Windows symbolic links need an elevated prompt or Developer Mode; junctions (/j) don't. | |||
| Use wildcards in file names | dir *.txt | gci *.txtGet-ChildItem -Filter *.txt | ls *.txt |
Gotcha: bash expands wildcards before the command runs (so a command never sees *.txt), and it's case-sensitive. On Windows, *.htm can also match .html files where short 8.3 names exist, because those are checked too; that applies to dir and -Filter. | |||
Reading files, finding text, sorting, counting and comparing.
| Task | CMD | PowerShell | Linux (bash) |
|---|---|---|---|
| Show a file's contents | type file.txt | cat file.txtGet-Content file.txtAliases: gc, cat, type | cat file.txt |
Gotcha: Get-Content returns one string per line (objects you can index: (gc f)[0]); use -Raw for the whole file as one string. | |||
| Page through a long file | more file.txt | gc file.txt | moreGet-Content file.txt | Out-Host -Paging | less file.txt |
Gotcha: more only goes forward; less scrolls both ways and searches with /. Out-Host -Paging doesn't work in the PowerShell ISE. | |||
| Show the first lines of a file | No built-in way; use powershell -c "Get-Content file.txt -TotalCount 10". | gc file.txt -Head 10Get-Content file.txt -TotalCount 10-Head and -First are aliases of -TotalCount. | head -n 10 file.txt |
Gotcha: head defaults to 10 lines; Get-Content has no default and reads the whole file without -TotalCount. | |||
| Show the last lines of a file | No built-in way; use powershell -c "Get-Content file.txt -Tail 10". | gc file.txt -Tail 10Get-Content file.txt -Tail 10 | tail -n 10 file.txt |
Gotcha: tail defaults to 10 lines; Get-Content -Tail needs a number. | |||
| Watch a log file as it grows | No built-in way; use PowerShell's Get-Content -Wait. | gc app.log -Tail 20 -WaitGet-Content app.log -Tail 20 -Wait | tail -f app.log |
Gotcha: Get-Content -Wait checks for new lines about once a second. tail -F (capital) also follows a log after it's rotated; tail -f stays on the old file. Stop either with Ctrl+C. | |||
| Find lines containing text | findstr /i "error" app.log | sls error app.logSelect-String -Pattern error -Path app.logAliases: sls | grep -i error app.log |
Gotcha: Select-String ignores case by default (-CaseSensitive to match it); grep and findstr are case-sensitive unless you add -i or /i. findstr's regular expressions are very limited; Select-String uses .NET regex and grep POSIX (grep -E or -P for more). | |||
| Find text in all files under a folder | findstr /s /i "error" *.log | gci -r -Filter *.log | sls errorGet-ChildItem -Recurse -Filter *.log | Select-String error | grep -ri error --include='*.log' . |
Gotcha: findstr /s and GNU grep -r search below the current folder when none is given. Select-String output includes the file name and line number, like grep -rn. | |||
| The two find commands | find "error" app.logCMD's find searches for text in files. | find.exe "error" app.logThere's no find alias, so this is CMD's text search. | find /var/log -name '*.log'Linux find searches for files. |
Gotcha: Same name, different jobs: on Windows find looks for text inside files; on Linux find looks for files. Under WSL or Git Bash on Windows, which one runs depends on your PATH. | |||
| Sort lines | sort names.txt | gc names.txt | sortGet-Content names.txt | Sort-ObjectAliases: sort | sort names.txt |
Gotcha: Sort-Object can sort objects by property (Sort-Object Length). sort orders by the current locale; LC_ALL=C sort gives plain byte order. The sort alias exists only on Windows; on Linux PowerShell runs the native sort. | |||
| Remove duplicate lines | No documented option in sort.exe; use PowerShell. | gc names.txt | sort -UniqueGet-Content names.txt | Sort-Object -Unique | sort -u names.txt |
Gotcha: uniq and Get-Unique only drop duplicates that are next to each other, so sort first. Sort-Object -Unique ignores case; sort -u doesn't. | |||
| Count lines (or words) | find /c /v "" file.txt | gc file.txt | measure -LineGet-Content file.txt | Measure-Object -Line -Word -Character | wc -l file.txt |
Gotcha: find /c /v "" counts lines that don't match nothing, i.e. all of them. (Get-Content file.txt).Count also counts lines. wc -l counts newline characters, so a last line without one isn't counted. | |||
| Compare two files | fc a.txt b.txtfc /b compares binary files. | Compare-Object (gc a.txt) (gc b.txt)Aliases: compare, diff | diff a.txt b.txtdiff -u for the unified format. |
Gotcha: Compare-Object compares sets of lines, not positions: it reports lines only in one file (<= or =>), ignoring order unless you add -SyncWindow 0. diff and fc show where the changes are. The Text Diff tool on this site shows both side by side. The diff alias exists only on Windows; on Linux PowerShell runs the native diff. | |||
| Show a folder tree | tree /f | tree /ftree.com works in PowerShell too; there's no cmdlet. | treeOften not installed: apt install tree. find . works everywhere. |
Gotcha: Without /f, Windows tree shows folders only; Linux tree shows files by default (tree -d for folders only). | |||
| Replace text in a file | No built-in way; use PowerShell or a text editor. | (gc f.txt) -replace 'old','new' | sc f.txt(Get-Content f.txt) -replace 'old','new' | Set-Content f.txt | sed -i 's/old/new/g' f.txt |
Gotcha: Keep the parentheses in PowerShell: they read the whole file before Set-Content writes it, otherwise the file is emptied while being read. -replace is a regex and replaces every match. On macOS, sed -i needs an argument: sed -i '' 's/old/new/g' f.txt. | |||
| Pick a column out of CSV or delimited text | for /f "tokens=2 delims=," %a in (users.csv) do @echo %a | Import-Csv users.csv | select NameImport-Csv users.csv | Select-Object Name | cut -d, -f2 users.csvawk -F, '{print $2}' for more control. |
Gotcha: Import-Csv understands quoted fields and headers; cut, awk and for /f split on every comma, even inside quotes. Use the CSV / JSON converter on this site for messy files. | |||
Running and stopping programs, background jobs, services and schedules.
| Task | CMD | PowerShell | Linux (bash) |
|---|---|---|---|
| List running processes | tasklist | gpsGet-ProcessAliases: gps, ps | ps aux |
Gotcha: Get-Process objects can be sorted and filtered (gps | sort CPU -Desc | select -First 10); ps aux is text. top or htop on Linux gives a live view, like Task Manager. The ps alias exists only on Windows; on Linux PowerShell runs the native ps. | |||
| Find a specific process | tasklist /fi "imagename eq notepad.exe" | gps notepadGet-Process -Name notepad | pgrep -a notepad |
Gotcha: Get-Process -Name takes the name without .exe; tasklist filters need it. ps aux | grep notepad also matches the grep itself; pgrep doesn't. | |||
| Stop a process | taskkill /im notepad.exe /ftaskkill /pid 1234 by process ID. | kill -Name notepad -ForceStop-Process -Name notepad -ForceAliases: kill, spps | kill 1234pkill notepad by name; kill -9 to force. |
Gotcha: Linux kill politely asks a process to exit (SIGTERM) and kill -9 forces it; Windows' taskkill without /f asks, with /f forces. PowerShell's kill is Stop-Process and takes -Id or -Name, not -9. | |||
| Start a program or open a file | start notepadstart report.pdf opens it in the default app. | start notepadStart-Process notepadAliases: start, saps Invoke-Item report.pdf (ii) opens a file. | xdg-open report.pdfmacOS: open report.pdf |
Gotcha: In CMD, start's first quoted argument is the window title: start "" "C:\My App\app.exe". Start-Process doesn't have that trap. | |||
| Run a command in the background | start /b backup.cmd | Start-Job { .\backup.ps1 }PowerShell 7: .\backup.ps1 & (Get-Job, Receive-Job). | ./backup.sh &jobs, fg and bg manage it; nohup keeps it running after logout. |
Gotcha: A background bash job dies when the terminal closes unless you use nohup, setsid or tmux. PowerShell jobs run in a separate process, so they don't see variables from your session ($using:var does). The & background operator was added in PowerShell 6. | |||
| List services | sc querynet start lists running ones. | Get-ServiceAliases: gsv | systemctl list-units --type=service |
Gotcha: Get-Service lists stopped services too; sc query only running ones unless you add state= all. | |||
| Start, stop or restart a service | net start Spoolernet stop Spooler; sc start / sc stop also work. | Restart-Service SpoolerStart-Service, Stop-Service. | sudo systemctl restart cupsstart, stop, status. |
Gotcha: In Windows PowerShell, sc is an alias of Set-Content, not the service tool: type sc.exe. Windows services have a name and a display name; the cmdlets accept either with -Name or -DisplayName. | |||
| Make a service start at boot | sc config Spooler start= auto | Set-Service Spooler -StartupType Automatic | sudo systemctl enable cups--now starts it as well. |
Gotcha: sc needs the space after the equals sign (start= auto); without it the command fails. systemctl enable doesn't start the service until you add --now or reboot. | |||
| Run something on a schedule | schtasks /create /sc daily /st 02:00 /tn Backup /tr C:\Scripts\backup.cmd | Register-ScheduledTask -TaskName Backup -Action (New-ScheduledTaskAction -Execute C:\Scripts\backup.cmd) -Trigger (New-ScheduledTaskTrigger -Daily -At 2am) | crontab -eThen a line like: 0 2 * * * /home/ann/backup.sh |
Gotcha: Scheduled tasks and cron jobs run without your session: no mapped drives, a minimal PATH, and a different current folder. Use full paths. The Cron Expression tool on this site explains cron schedules. | |||
| List scheduled tasks | schtasks /query | Get-ScheduledTask | crontab -lsystemctl list-timers for systemd timers. |
Gotcha: crontab -l shows only your own jobs; system jobs live in /etc/crontab, /etc/cron.d and systemd timers. | |||
| Wait a few seconds | timeout /t 5 | Start-Sleep 5Start-Sleep -Seconds 5Aliases: sleep | sleep 5 |
Gotcha: timeout fails when input is redirected (in some scheduled tasks); add /nobreak, or use ping -n 6 127.0.0.1 >nul as the old workaround. | |||
Addresses, connectivity, DNS, ports, routes and downloads.
| Task | CMD | PowerShell | Linux (bash) |
|---|---|---|---|
| Show IP addresses | ipconfigipconfig /all for DNS servers, MAC and DHCP. | Get-NetIPConfigurationGet-NetIPAddress for every address. | ip addrAliases: ip a |
Gotcha: ifconfig is the old Linux command (net-tools) and is missing on many current distros; ip addr replaces it. On Linux ipconfig doesn't exist at all. | |||
| Ping a host | ping example.comping -t to keep going, -n 10 for ten. | Test-Connection example.com-Count 10; PowerShell 7: -Repeat. | ping -c 4 example.com |
Gotcha: Windows ping stops after 4 pings; Linux ping keeps going until Ctrl+C unless you give -c. Windows PowerShell's Test-Connection returns objects and is slow to time out. | |||
| Trace the route to a host | tracert example.com | Test-NetConnection example.com -TraceRoute | traceroute example.comtracepath needs no root; mtr combines ping and traceroute. |
Gotcha: tracert uses ICMP; Linux traceroute sends UDP by default, which firewalls treat differently (traceroute -I for ICMP). | |||
| Look up a DNS name | nslookup example.com | Resolve-DnsName example.com-Type MX for mail servers. | dig example.comdig MX example.com; host example.com is shorter. |
Gotcha: nslookup has its own resolver logic and can disagree with what applications see; Resolve-DnsName uses the Windows resolver (hosts file and cache included). The DNS Lookup tool on this site compares public resolvers. | |||
| Clear the DNS cache | ipconfig /flushdns | Clear-DnsClientCache | resolvectl flush-cachesOn systemd-resolved systems; others may have no cache. |
Gotcha: Many Linux systems don't cache DNS at all unless systemd-resolved, dnsmasq or nscd is running, so there may be nothing to flush. | |||
| Show network connections | netstat -ano | Get-NetTCPConnection | ss -tunapnetstat is the older net-tools command. |
Gotcha: netstat -ano shows the process ID; netstat -b shows the program name but needs an elevated prompt. ss -p only shows processes you own unless run with sudo. | |||
| See what's listening on a port | netstat -ano | findstr :443 | Get-NetTCPConnection -LocalPort 443 -State ListenOwningProcess is the PID: Get-Process -Id … | sudo ss -ltnp 'sport = :443'Or: sudo lsof -i :443 |
Gotcha: findstr :443 also matches :4430 and remote ports; filter more tightly or use Get-NetTCPConnection. | |||
| Test whether a TCP port is reachable | No built-in way (the telnet client is an optional feature); use PowerShell's Test-NetConnection. | tnc example.com -Port 443Test-NetConnection example.com -Port 443Aliases: tnc | nc -zv example.com 443 |
Gotcha: Test-NetConnection pings first, which adds a delay and a misleading warning when ICMP is blocked; TcpTestSucceeded is the answer that matters. | |||
| Download a file | curl -o file.zip https://example.com/file.zipcurl.exe is built into Windows 10 1803 and later. | iwr https://example.com/file.zip -OutFile file.zipInvoke-WebRequest https://example.com/file.zip -OutFile file.zipAliases: iwr | curl -O https://example.com/file.zipwget https://… also works where installed. |
Gotcha: In Windows PowerShell 5.1, curl and wget are aliases of Invoke-WebRequest, so curl -o fails with a parameter error: type curl.exe. PowerShell 7 removed those aliases. Windows PowerShell 5.1's Invoke-WebRequest is slow with its progress bar on: $ProgressPreference = 'SilentlyContinue' speeds it up. | |||
| See a website's response headers | curl -I https://example.com | (iwr https://example.com -Method Head).Headers(Invoke-WebRequest https://example.com -Method Head).Headers | curl -I https://example.com |
Gotcha: curl -I sends a HEAD request, which some servers answer differently from a normal GET; curl -sD - -o /dev/null URL shows the headers of a real GET. The Security Headers tool on this site grades them. | |||
| Find your public IP address | curl https://tclient.com/ip | irm https://tclient.com/ipInvoke-RestMethod https://tclient.com/ip | curl https://tclient.com/ip |
Gotcha: ipconfig and ip addr show your private address on the local network; behind a router, the internet sees the router's public address instead. | |||
| Show the computer's name | hostname | hostnameAlso $env:COMPUTERNAME. | hostnamehostnamectl shows and sets it on systemd systems. |
Gotcha: %COMPUTERNAME% and $env:COMPUTERNAME are upper case and limited to 15 characters (the NetBIOS name); hostname gives the full name. | |||
| Show the ARP table (IP to MAC) | arp -a | Get-NetNeighbor | ip neigharp -n on systems with net-tools. |
Gotcha: Entries only exist for hosts you've recently talked to; ping a host first to see its MAC address. | |||
| Show the routing table | route print | Get-NetRoute | ip routeroute -n on systems with net-tools. |
Gotcha: route print lists IPv4 and IPv6 together; ip route shows IPv4 only (ip -6 route for IPv6). | |||
Who you are, accounts and groups, running as administrator, file permissions.
| Task | CMD | PowerShell | Linux (bash) |
|---|---|---|---|
| Show who you're logged in as | whoami | whoamiAlso $env:USERNAME. | whoamiid shows the user and group IDs too. |
Gotcha: Windows whoami prints DOMAIN\user; whoami /upn and /fqdn give other forms. $env:USERNAME doesn't include the domain. | |||
| Show your group memberships | whoami /groups | whoami /groups | idgroups lists names only. |
Gotcha: Group changes only apply after you log on again on both systems (on Linux, newgrp or a new session). | |||
| List local user accounts | net user | Get-LocalUser | getent passwdcut -d: -f1 /etc/passwd for names only. |
Gotcha: getent passwd includes dozens of system accounts; real users usually have a UID of 1000 or more. Get-LocalUser is part of Windows PowerShell 5.1; PowerShell 7 may load it through Windows PowerShell compatibility and show a notice. | |||
| Create a local user | net user bob * /addThe * prompts for the password. | New-LocalUser bob -Password (Read-Host -AsSecureString) | sudo useradd -m bob && sudo passwd bobDebian/Ubuntu: sudo adduser bob asks for everything. |
Gotcha: Don't type the password on the command line (net user bob P@ss /add): it ends up in history and in process listings. | |||
| Add a user to a group | net localgroup Administrators bob /add | Add-LocalGroupMember -Group Administrators -Member bob | sudo usermod -aG sudo bobThe admin group is wheel on Fedora and RHEL. |
Gotcha: usermod -G without -a replaces all the user's groups instead of adding one. The new membership applies at the user's next logon. | |||
| Run a command as administrator | sudo cmdWindows 11 24H2 and later, once enabled. Otherwise: right-click, Run as administrator. | Start-Process pwsh -Verb RunAs | sudo commandsudo -i for a root shell. |
Gotcha: runas /user:Administrator switches to another account; it doesn't elevate your own. Sudo for Windows is off by default: turn it on in Settings > System > Advanced (Enable sudo). | |||
| Check whether you're elevated | net session >nul 2>&1 && echo Elevated | ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole('Administrators') | [ "$(id -u)" -eq 0 ] && echo root |
Gotcha: Being in the Administrators group isn't enough on Windows: with UAC the prompt only has admin rights after Run as administrator. | |||
| View file permissions | icacls file.txt | Get-Acl file.txt | flGet-Acl file.txt | Format-List | ls -l file.txtgetfacl for extended ACLs. |
Gotcha: Windows uses ACLs of allow and deny entries per user or group, inherited from folders; Linux shows read, write and execute for owner, group and everyone else (rwxr-x---). | |||
| Change file permissions | icacls file.txt /grant bob:(R) | icacls file.txt /grant 'bob:(R)'Set-Acl works with an ACL object you build first. | chmod 640 file.txtchmod u+x script.sh adds execute for the owner. |
Gotcha: In PowerShell, quote icacls permissions: (R) is otherwise read as a PowerShell expression. chmod numbers are owner/group/other: 6 = read+write, 4 = read, 0 = nothing. | |||
| Change a file's owner | icacls file.txt /setowner bobtakeown /f file.txt makes you the owner. | icacls file.txt /setowner bob | sudo chown bob:staff file.txtchown -R for a whole folder. |
Gotcha: chown takes user:group in one argument; on Windows the group isn't set this way. Both need administrator or root rights to give a file away. | |||
Free space, OS and hardware details, environment variables, time, power and history.
| Task | CMD | PowerShell | Linux (bash) |
|---|---|---|---|
| Show free disk space | dir C:\The last line shows bytes free. | Get-VolumeGet-PSDrive C for one drive. | df -h |
Gotcha: df -h shows every mounted filesystem, including small temporary ones (tmpfs); df -h / for just the root. | |||
| Show a folder's total size | dir /s D:\DataThe total is at the end. | (gci D:\Data -r -File | measure Length -Sum).Sum / 1GB(Get-ChildItem D:\Data -Recurse -File | Measure-Object Length -Sum).Sum / 1GB | du -sh /data |
Gotcha: du shows space used on disk, which differs from the sum of file sizes (block size, sparse files); Get-ChildItem sums the file lengths. | |||
| Show system information | systeminfo | Get-ComputerInfo | hostnamectluname -a for the kernel; cat /etc/os-release for the distribution. |
Gotcha: Get-ComputerInfo is slow (it collects everything); pick what you need: Get-ComputerInfo -Property OsName,OsVersion. | |||
| Show the OS version | ver | [Environment]::OSVersion$PSVersionTable shows the PowerShell version. | cat /etc/os-releaselsb_release -a where installed. |
Gotcha: ver shows the build number (10.0.26100) but not the edition or the marketing name; winver shows those in a window. | |||
| Show how long the system has been up | systeminfo | find "Boot Time" | Get-UptimeWindows PowerShell: (Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime | uptime |
Gotcha: With Fast Startup on, shutting down Windows doesn't reset the uptime; only a restart does. Get-Uptime was added in PowerShell 6. | |||
| Show CPU and memory | systeminfo | findstr /c:"Total Physical Memory" | Get-CimInstance Win32_ProcessorGet-CimInstance Win32_OperatingSystem | select FreePhysicalMemory, TotalVisibleMemorySize | lscpufree -h for memory. |
Gotcha: wmic, the old CMD way, is deprecated and being removed from Windows; use the CIM cmdlets. Linux free counts cache as "used"; the available column is what matters. | |||
| List environment variables | set | gci env:Get-ChildItem Env: | printenvAliases: env |
Gotcha: set in bash also prints shell variables and functions; printenv shows only exported variables. | |||
| Read an environment variable | echo %PATH% | $env:PATH | echo $PATH |
Gotcha: Linux variable names are case-sensitive ($PATH, not $path), and PATH entries are separated by : rather than ;. | |||
| Set an environment variable | set NAME=valuesetx NAME value saves it for new windows. | $env:NAME = 'value'[Environment]::SetEnvironmentVariable('NAME','value','User') saves it. | export NAME=valueAdd it to ~/.bashrc to keep it. |
Gotcha: In CMD, set NAME = value creates a variable called "NAME " with the value " value": no spaces around =. setx doesn't change the current window and cuts values at 1,024 characters. | |||
| Show the date and time | echo %DATE% %TIME%date /t and time /t | Get-DateGet-Date -Format o for ISO 8601. | datedate -u for UTC; date +%F for 2026-10-26. |
Gotcha: %DATE% follows the regional settings, so scripts that cut it apart break on other machines. Get-Date returns a real date you can calculate with. The Unix Timestamp tool on this site converts between formats. | |||
| Restart the computer | shutdown /r /t 0 | Restart-Computer | sudo rebootsystemctl reboot |
Gotcha: shutdown without /t waits 30 seconds on Windows; Linux shutdown -r without a time waits one minute (shutdown -r now). | |||
| Shut down the computer | shutdown /s /t 0 | Stop-Computer | sudo shutdown -h nowsystemctl poweroff |
Gotcha: With Fast Startup on, Windows shutdown /s hibernates the kernel; shutdown /s /full does a full shutdown. | |||
| Clear the screen | cls | clsClear-HostAliases: cls, clear | clearCtrl+L |
| Show or search command history | doskey /historyF7 shows a list. | hGet-HistoryAliases: h, history Ctrl+R searches history (PSReadLine). | historyCtrl+R searches backwards. |
Gotcha: Get-History only covers the current session; PSReadLine keeps everything in a file ((Get-PSReadLineOption).HistorySavePath). CMD forgets history when the window closes. | |||
Variables, quoting, conditions, loops, functions, exit codes, pipes and redirection.
| Task | CMD | PowerShell | Linux (bash) |
|---|---|---|---|
| Write a comment | rem commentAliases: :: comment | # comment<# … #> for a block. | # comment |
Gotcha: :: is a label trick, not a real comment: inside a parenthesised block it can cause errors, so use rem there. | |||
| Set and use a variable | set name=Ann … echo %name% | $name = 'Ann' … $name | name=Ann … echo "$name" |
Gotcha: bash allows no spaces around = (name = Ann runs a command called name). In CMD, variables inside a ( … ) block are expanded once when the block is read; use setlocal enabledelayedexpansion and !name!. | |||
| Quote a string | echo "Hello %name%"Escape special characters with ^ outside quotes. | "Hello $name" vs 'Hello $name'Escape with a backtick: `$ | "Hello $name" vs 'Hello $name'Escape with a backslash: \$ |
Gotcha: CMD has only double quotes and passes them on to the program. In PowerShell and bash, single quotes are literal and double quotes expand variables, but the escape character differs (` vs \). | |||
| If / else | if exist file.txt (echo yes) else (echo no) | if (Test-Path file.txt) { 'yes' } else { 'no' } | if [ -f file.txt ]; then echo yes; else echo no; fi |
Gotcha: bash needs the spaces inside [ ] and the closing fi. In CMD, else must be on the same line as the closing parenthesis. | |||
| Compare numbers and strings | if %n% GEQ 8 echo bigEQU NEQ LSS LEQ GTR GEQ; if /i for case-insensitive strings. | if ($n -ge 8) { 'big' }-eq -ne -lt -le -gt -ge; -like and -match for patterns. | if [ "$n" -ge 8 ]; then echo big; fi[[ $s == foo* ]] for patterns. |
Gotcha: In PowerShell and CMD, > is redirection, not "greater than": if ($n > 8) creates a file named 8. PowerShell's -eq ignores case for strings (-ceq doesn't). | |||
| Loop over files | for %f in (*.txt) do echo %fWrite %%f inside a .bat file. | foreach ($f in gci *.txt) { $f.Name }Get-ChildItem *.txt | ForEach-Object { $_.Name }Aliases: %, foreach | for f in *.txt; do echo "$f"; done |
Gotcha: The single % works at the prompt but batch files need %%f. In bash, quote "$f" or names with spaces split into several words. | |||
| Loop a number of times | for /l %i in (1,1,10) do echo %i | foreach ($i in 1..10) { $i } | for i in {1..10}; do echo $i; done |
Gotcha: CMD's /l takes start, step, end; 1..10 and {1..10} include both ends. | |||
| While loop | :loop … if %n% LSS 5 goto loopCMD has no while; use goto. | while ($n -lt 5) { $n++ } | while [ $n -lt 5 ]; do n=$((n+1)); done |
Gotcha: bash arithmetic needs $(( )); n=n+1 assigns the text "n+1". | |||
| Define and call a function | call :greet Ann … :greet echo Hello %1 goto :eof | function Greet($Name) { "Hello $Name" } … Greet Ann | greet() { echo "Hello $1"; } … greet Ann |
Gotcha: Call PowerShell functions like commands: Greet Ann Bob, not Greet('Ann','Bob'), which passes a single array. In CMD, end the function with goto :eof or it runs into the next label. | |||
| Read a script's arguments | %1 %2 … %* | param($Path) or $args[0] | $1 $2 … "$@" |
Gotcha: %~1 removes surrounding quotes in CMD. In bash, "$@" keeps each argument intact; $* joins them. | |||
| Check the last exit code | echo %ERRORLEVEL%exit /b 3 sets it. | $LASTEXITCODE$? is $true or $false; exit 3 sets it. | echo $?exit 3 sets it. |
Gotcha: $? is true/false in PowerShell but a number in bash. $LASTEXITCODE only changes when a native program runs, not after cmdlets. robocopy is the exception everywhere: codes below 8 mean success. | |||
| Run a script file | backup.batFrom another batch file: call backup.bat | .\backup.ps1If blocked: Set-ExecutionPolicy RemoteSigned -Scope CurrentUser | ./backup.shAfter chmod +x backup.sh, or: bash backup.sh |
Gotcha: PowerShell won't run a script from the current folder without .\, and execution policy blocks scripts on Windows clients by default. A batch file that runs another without call never returns. bash scripts need a #!/bin/bash first line and the execute bit. Windows Server's default execution policy is RemoteSigned; Windows client editions default to Restricted. | |||
| Pipe one command into another | dir | find ".txt" | gci | where Length -gt 1MBGet-ChildItem | Where-Object Length -gt 1MBAliases: where, ? | ls -l | grep .txt |
Gotcha: The big one: PowerShell pipes objects, so you filter on properties (Length, Name) instead of parsing text. CMD and bash pipe lines of text. Mixing them in PowerShell (gci | findstr txt) works, but findstr sees formatted text, not objects. | |||
| Send output (and errors) to a file | cmd > out.txt 2>&1>> appends. | cmd *> out.txtOut-File -Encoding utf8 to choose the encoding. | cmd > out.txt 2>&1&> out.txt is the short form. |
Gotcha: Windows PowerShell 5.1's > writes UTF-16 LE, which many tools can't read; PowerShell 7 writes UTF-8 without a BOM; CMD writes the console code page. In bash, 2>&1 must come after > out.txt. *> (all streams) needs PowerShell 3 or later. | |||
| Throw output away | cmd > nul 2>&1 | cmd | Out-NullOr > $null | cmd > /dev/null 2>&1 |
Gotcha: In PowerShell, > nul creates a file only on Linux; $null works everywhere. | |||
| Run the next command only if the first worked (&& and ||) | build && deploy || echo failed | build && deploy || 'failed'PowerShell 7 and later. | build && deploy || echo failed |
Gotcha: Windows PowerShell 5.1 doesn't support && and ||; use if ($?) { … } there, or ; to run commands regardless. && and || were added in PowerShell 7.0. | |||
| Store a command's output in a variable | for /f %i in ('hostname') do set h=%i | $h = hostname | h=$(hostname) |
Gotcha: PowerShell captures objects (or an array of lines from native programs); bash captures text and strips trailing newlines. | |||
| Ask the user for input | set /p name=Name? | $name = Read-Host 'Name?' | read -p 'Name? ' name |
Gotcha: Read-Host -AsSecureString hides typing (passwords); read -s does the same in bash. | |||
Finding, installing, upgrading and removing software from the command line.
| Task | CMD | PowerShell | Linux (bash) |
|---|---|---|---|
| Search for a package | winget search vscodechoco search vscode with Chocolatey. | winget search vscodeFind-Module for PowerShell modules. | apt search vscode |
Gotcha: apt searches the package lists you downloaded last; run sudo apt update first on a fresh system. Fedora/RHEL: dnf search. Arch: pacman -Ss. macOS: brew search. | |||
| Install a package | winget install Microsoft.VisualStudioCode | winget install Microsoft.VisualStudioCodeInstall-Module for PowerShell modules. | sudo apt install code |
Gotcha: Use the exact winget ID (--id … --exact) in scripts: names can match several packages. apt needs sudo; with winget, installers that need administrator rights show a UAC prompt. Fedora/RHEL: sudo dnf install. Arch: sudo pacman -S. macOS: brew install (no sudo). | |||
| Upgrade everything | winget upgrade --all | winget upgrade --allUpdate-Module for PowerShell modules. | sudo apt update && sudo apt upgrade |
Gotcha: apt update only refreshes the lists; apt upgrade installs the updates. winget upgrade without --all only lists what's available. Fedora/RHEL: sudo dnf upgrade. Arch: sudo pacman -Syu. macOS: brew upgrade. | |||
| Remove a package | winget uninstall Microsoft.VisualStudioCode | winget uninstall Microsoft.VisualStudioCodeUninstall-Module for PowerShell modules. | sudo apt remove codeapt purge also deletes its system settings. |
Gotcha: apt remove keeps configuration files; purge deletes them. Neither touches files in your home folder. Fedora/RHEL: sudo dnf remove. Arch: sudo pacman -R. macOS: brew uninstall. | |||
| List installed packages | winget list | winget listGet-Package (Windows PowerShell) also lists MSI installs. | apt list --installeddpkg -l is the older form. |
Gotcha: winget list shows everything in Apps & features, including software it didn't install; the Source column is filled in when winget can match an entry to a repository package. Fedora/RHEL: dnf list installed or rpm -qa. Arch: pacman -Q. macOS: brew list. | |||
| Show details of a package | winget show Microsoft.VisualStudioCode | winget show Microsoft.VisualStudioCode | apt show code |
Gotcha: apt show describes the version in the repository, which may be newer than the one installed (apt policy code shows both). Fedora/RHEL: dnf info. Arch: pacman -Si. macOS: brew info. | |||
Getting help on a command and finding the command you need.
| Task | CMD | PowerShell | Linux (bash) |
|---|---|---|---|
| Get help on a command | dir /?help dir does the same. | Get-Help Get-ChildItem -FullAliases: help, man Get-ChildItem -? shows a summary. | man lsls --help for a quick summary. |
Gotcha: PowerShell's help is nearly empty until you run Update-Help (as administrator in Windows PowerShell 5.1); -Online opens the Microsoft Learn page instead. PowerShell 7 can install help per user: Update-Help -Scope CurrentUser. | |||
| See examples of a command | /? shows few examples; Microsoft Learn's command reference has more. | Get-Help Get-ChildItem -Examples | man lsLook for the EXAMPLES section; tldr ls gives short examples if installed. |
Gotcha: Many GNU man pages have no examples at all; info pages (info ls) are often more detailed. | |||
| Find a command by what it does | helpLists the built-in CMD commands. | Get-Command *service*Aliases: gcm Get-Command -Verb Get -Noun *net* | apropos servicecompgen -c lists every command. |
Gotcha: PowerShell cmdlets follow Verb-Noun names, so guessing works: Get-*, Set-*, *-Service. apropos searches man page descriptions and needs mandb to have run. | |||
| Find where a command lives | where notepad | Get-Command notepadAliases: gcm where.exe notepad | which notepadtype -a notepad also shows aliases and functions. |
Gotcha: In PowerShell, where is an alias of Where-Object, so where notepad silently does nothing: type where.exe. Get-Command also tells you whether a name is an alias, function, cmdlet or program. | |||
Opening, saving and quitting the usual terminal editors.
| Task | CMD | PowerShell | Linux (bash) |
|---|---|---|---|
| Open a file in an editor | notepad file.txt | notepad file.txtcode file.txt opens VS Code where installed. | nano file.txtvim file.txt, or vi on minimal systems. |
Gotcha: The old DOS EDIT command was 16-bit and isn't in 64-bit Windows. On a Linux server without a desktop, nano is the friendliest choice; vi is almost always installed. | |||
| Save and quit | Ctrl+S, then Alt+F4In Notepad. | Ctrl+S, then Alt+F4In Notepad. | nano: Ctrl+O, Enter, Ctrl+Xvim: Esc, then :wq and Enter |
Gotcha: vim starts in normal mode: press i to type, Esc to stop typing, then : commands. nano lists its shortcuts at the bottom (^ means Ctrl). | |||
| Quit without saving | Alt+F4, then Don't Save | Alt+F4, then Don't Save | vim: Esc, then :q! and Enternano: Ctrl+X, then N |
Gotcha: :q on its own refuses to quit when there are changes; the ! discards them. | |||
| Search inside the editor | Ctrl+FIn Notepad. | Ctrl+FIn Notepad. | nano: Ctrl+Wvim: /text then n for the next match |
Gotcha: vim's / takes a regular expression, so special characters such as . and * need a backslash to be literal. | |||
| Edit a protected system file | Run as administrator, then notepad C:\Windows\System32\drivers\etc\hosts | Start-Process notepad C:\Windows\System32\drivers\etc\hosts -Verb RunAs | sudo nano /etc/hostssudoedit /etc/hosts uses your own editor settings. |
Gotcha: Notepad opened without elevation shows the file but can't save it (it offers Save As instead). The hosts file is in a different place on each OS. | |||