Cloud Explainers

PowerShell vs Bash: Which One Should You Learn?

PowerShell10 min videoPublished Nov 5, 2026

PowerShell vs Bash for beginners, tested side by side on one Ubuntu laptop: Ana does the same jobs in both shells. Finding the three biggest log files shows the real difference: Bash's pipe carries text, so awk cut "green tea.log" down to "green", while PowerShell's pipe carries file objects with named properties. Then JSON from a web page (jq vs Invoke-RestMethod), start-up time measured, and why ls means something different in PowerShell on Linux and on Windows. Ends with a table for picking which one to learn first.

PowerShell vs Bash: Which One Should You Learn?, the full picture: diagram of Bash window, PowerShell window, Shop logs, Orders page, Ana's laptop: Ubuntu 24.04.5 LTS
The whole picture, from the video's recap.

Two shells, one question

You want to learn the command line, and every guide says Bash or PowerShell, but never which one first. One tutorial types ls, the next one types Get-ChildItem, and you're not sure which world you're in. So Ana does the same jobs in both shells, on one laptop, and we compare. Along the way, a command you already know, ls, will do something you might not expect in PowerShell, and we'll see why.

Bash or PowerShell?: diagram of You, new to the terminal, Bash, PowerShell

You're not the only one asking. On the freeCodeCamp forum, a beginner wrote: Should I learn PowerShell or Bash? Their goal was a web app in JavaScript, and later a mobile app. We'll answer them at the end, with what we measure.

A real question from a beginners' forum: diagram of A beginner, The forum, "Should I learn PowerShell or Bash?", my goal: a web app in JavaScript, then a mobile app, our answer comes at the end, freeCodeCamp forum, 2018

What a shell is

What is a shell, exactly? It's the program that reads what you type, runs other programs for you, and passes one program's results to the next. Bash and PowerShell both do that job. Bash is the GNU shell, the usual default on Linux. PowerShell is Microsoft's, built into Windows, and also free on Linux and Mac.

A shell sits between you and the system: diagram of You, The shell, Programs, Operating system

If you're on Windows, which PowerShell do you have? Windows comes with Windows PowerShell 5.1, the older one, blue window and all. PowerShell 7 is the newer one: free, open source, installed separately, and the same on Windows, Linux and Mac. Everything in this test uses PowerShell 7, the one Microsoft keeps improving.

Two PowerShells on Windows: cards of Windows PowerShell 5.1, PowerShell 7, Comes with every Windows, Same on Windows, Linux, Mac

Here's a fresh Windows Server 2022 from our lab. Ask PowerShell its version, stored in a built-in variable: five point one. Ask for pwsh, the PowerShell 7 program, and Windows says it isn't recognized. It isn't there until you install it.

Windows Server 2022: Windows PowerShell 5.1
PS C:\shop\logs> $PSVersionTable.PSVersion.ToString()5.1.20348.558
Windows Server 2022: Windows PowerShell 5.1
PS C:\shop\logs> Get-Command pwshGet-Command : The term 'pwsh' is not recognized as the name of a cmdlet, function, script file, or
operable program. Check the spelling of the name, or if a path was included, verify that the path
is correct and try again.

Their commands look very different. Here's how Bash sorts by size: sort, dash k five, dash n, dash r. Dash k five means: sort by the fifth column of text. Dash n means read it as a number, and dash r means biggest first. Short, fast to type, and you have to know that size happens to be column five.

Bash: short flags, counted columns: diagram of sort -k5 -n -r, sort = the program, -k5 = column 5, -n = as a number, -r = biggest first, You must know that size is column 5

PowerShell's version reads almost like English: Sort-Object, Length, dash Descending. Every command is a verb and a noun, like Sort-Object or Get-ChildItem. Length isn't a column number. It's the name of a property: the file's size in bytes. Longer to type, but you can read it a year later.

PowerShell: a verb, a noun, a property name: diagram of Sort-Object Length -Descending, Sort = verb, Object = noun, Length = a property, -Descending = biggest first, Names, not positions

So here's our test. Ana runs a small tea shop. On her laptop there's a folder of shop logs, and a page that lists today's orders. On the left, a Bash window. On the right, a PowerShell window. Same files, same jobs.

Text or objects?

Job one: find the three biggest logs. In Bash, ls dash l lists the files, one line each. Notice the quotes ls puts around green tea dot log: that name has a space. Then a pipe, the bar symbol, sends those lines into sort, by column five, the size, and head keeps the first three. Correct so far.

laptop: Bash window
ls -ltotal 256
-rw-r--r-- 1 ana ana   1232 Nov  5 23:15  app.log
-rw-r--r-- 1 ana ana 176363 Nov  5 23:15 'green tea.log'
-rw-r--r-- 1 ana ana  65130 Nov  5 23:15  orders.log
-rw-r--r-- 1 ana ana   9877 Nov  5 23:15  payments.log
laptop: Bash window
ls -l | sort -k5 -n -r | head -3-rw-r--r-- 1 ana ana 176363 Nov  5 23:15 green tea.log
-rw-r--r-- 1 ana ana  65130 Nov  5 23:15 orders.log
-rw-r--r-- 1 ana ana   9877 Nov  5 23:15 payments.log

Now Ana wants just the names. awk splits each line at spaces, and dollar nine prints the ninth piece: the name. Before we run it: what will it print for green tea dot log? It prints green. Half a file name, and no error to warn her.

laptop: Bash window
ls -l | sort -k5 -n -r | head -3 | awk '{print $9}'green
orders.log
payments.log

Why did that happen? Bash's pipe carries plain text. To awk, this line is just words with spaces between them. Size is word five, the date is six to eight, and the name should be word nine. But a space inside the name makes it two words: nine and ten. The meaning was lost the moment ls turned files into text.

To awk, a line is only words: diagram of … 176363 Nov 5 23:15 green tea.log, 176363 = word 5, Nov 5 23:15 = words 6 to 8, green = word 9, tea.log = word 10, The pipe carried text, not files

Same job in PowerShell. Get-ChildItem lists the folder. Sort-Object sorts by Length, and Select-Object keeps the first three, with their name and length. Wrap it all in brackets and add dot Name, and you get just the names. Green tea dot log comes back whole, space and all.

laptop: PowerShell window
PS /home/ana/shop/logs> Get-ChildItem | Sort-Object Length -Descending | Select-Object -First 3 Name, LengthName          Length
----          ------
green tea.log 176363
orders.log     65130
payments.log    9877
laptop: PowerShell window
PS /home/ana/shop/logs> (Get-ChildItem | Sort-Object Length -Descending | Select-Object -First 3).Namegreen tea.log
orders.log
payments.log

That's the whole difference in one picture. In Bash, each program prints text, and the next one has to cut it apart again. In PowerShell, the pipe carries objects: each file arrives as a file, with its name and length already labelled. Nobody counts columns.

What travels through the pipe: diagram of ls -l, sort, head, awk $9, Get-ChildItem, Sort-Object, .Name, Bash: text, PowerShell: objects

What's inside one of those objects? Get-Item fetches green tea dot log, and Format-List shows four of its properties: the name, the length, the extension, and when it was last changed. Ask its type, and it says FileInfo: a real file object from dot NET, the library PowerShell is built on.

One file, as a PowerShell object: cards of Name: green tea.log, Length: 176363, Extension:.log, Type: FileInfo

Reading data from the web

Does that matter beyond file names? Most of what you'll fetch from the web is JSON: text with labels, like id, customer, and total. Ana's orders page serves five orders that way. Job two: which orders are over twenty dollars?

One order, as JSON text: diagram of {"id": 1042, "total": 27.5}, "id": 1042 = id, "total": 27.5 = total, Job two: orders over $20

Bash sees JSON as one long string. curl fetches it, and to read the labels we need a helper program, jq, which Ana installed separately. Its little language says: for each order, keep the ones with a total over twenty, and print the id. Two orders: ten forty-two and ten forty-four.

laptop: Bash window
curl -s localhost:8080/orders.json | head -3[
 {"id": 1041, "customer": "Mei", "tea": "Sencha", "total": 13.0},
 {"id": 1042, "customer": "Tom", "tea": "Earl Grey", "total": 27.5},
laptop: Bash window
curl -s localhost:8080/orders.json | jq '.[] | select(.total > 20) | .id'1042
1044

In PowerShell, Invoke-RestMethod fetches the page and turns the JSON into objects, stored in a variable called orders. The first one prints as a neat row. Where-Object total, dash g t twenty, meaning greater than twenty, gives Tom and Lena. And Measure-Object adds all five totals: 110 dollars and 25 cents. No helper program needed.

laptop: PowerShell window
PS /home/ana/shop/logs> $orders = Invoke-RestMethod http://localhost:8080/orders.json
laptop: PowerShell window
PS /home/ana/shop/logs> $orders[0]  id customer tea    total
  -- -------- ---    -----
1041 Mei      Sencha 13.00
laptop: PowerShell window
PS /home/ana/shop/logs> $orders | Where-Object total -gt 20  id customer tea       total
  -- -------- ---       -----
1042 Tom      Earl Grey 27.50
1044 Lena     Matcha    42.00
laptop: PowerShell window
PS /home/ana/shop/logs> $orders | Measure-Object total -Sum | Select-Object Count, SumCount    Sum
-----    ---
    5 110.25

So on our map, both shells got the right answer from the orders page. Bash needed one more tool to read it. PowerShell read it on its own. So far, that's two jobs to PowerShell. Is Bash just the old way, then?

Where Bash wins

Not at all. First: speed to start. Ana starts each shell ten times and lets time measure it. Ten Bash starts: 0.012 seconds. Ten PowerShell starts: 1.28 seconds. Each PowerShell start loads the dot NET runtime first.

laptop: Bash window
time (for i in {1..10}; do bash -c exit; done)real	0m0.012s
user	0m0.006s
sys	0m0.007s
laptop: Bash window
time (for i in {1..10}; do pwsh -NoProfile -c exit; done)real	0m1.280s
user	0m1.638s
sys	0m0.619s

That's about 110 times slower to start, around 128 milliseconds each. Typing commands, you'll never feel it. But a script that starts a shell for every file, thousands of times, will.

Ten shell starts on the same laptop (lower is better): chart of Bash 5.2.21: 0.012 s, PowerShell 7.6.9: 1.28 s, About 110x slower to start

Second: Bash is already there. Ana's Ubuntu image came with Bash; PowerShell she installed herself, from Microsoft's package repository. Most Linux servers and build scripts speak Bash, and so do most Linux guides. On Windows you can get it too, with Git Bash or the Windows Subsystem for Linux.

Bash is already everywhere Linux runs: cards of Came with Ana's Ubuntu image, PowerShell: installed by hand, Servers and build scripts, On Windows too

Third, the surprise from the start. In PowerShell on Linux, type ls, and you get the old text listing, not objects. An alias is a nickname for another command, and Get-Alias says there's no alias called ls here. Get-Command shows why: ls is the Linux program, in slash user slash bin. Its Windows cousin dir is still an alias for Get-ChildItem.

laptop: PowerShell window
PS /home/ana/shop/logs> Get-Alias lsGet-Alias: This command cannot find a matching alias because an alias with the name 'ls' does not exist.
laptop: PowerShell window
PS /home/ana/shop/logs> Get-Command ls | Format-List Name, CommandType, SourceName        : ls
CommandType : Application
Source      : /usr/bin/ls
laptop: PowerShell window
PS /home/ana/shop/logs> (Get-Alias dir).DefinitionGet-ChildItem

And on Windows? Same question to the same command: there, ls is an alias for Get-ChildItem. So ls, piped into Sort-Object, gives objects, and green tea dot log comes back whole.

Windows Server 2022: Windows PowerShell 5.1
PS C:\shop\logs> Get-Alias lsCommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           ls -> Get-ChildItem
Windows Server 2022: Windows PowerShell 5.1
PS C:\shop\logs> ls | Sort-Object Length -Descending | Select-Object -First 3 Name, LengthName          Length
----          ------
green tea.log 176363
orders.log     65130
payments.log    9877

On Windows, ls in PowerShell is a nickname for Get-ChildItem, and returns objects. On Linux, Microsoft leaves that nickname out, so it can't hide the real ls program. Same word, two different commands. So in scripts you share, write the full name: Get-ChildItem.

One command name, two meanings: diagram of You type, On Windows, On Linux, File objects, Lines of text

Which one should you learn?

Do real teams have to choose? Microsoft's own Azure Cloud Shell, the command line inside its cloud website, lets you pick Bash or PowerShell, and keeps the same files for both. Even Microsoft ships both, because its users need both.

Microsoft's own cloud offers both: cards of Azure Cloud Shell, Pick Bash or PowerShell, One file share for both, Teams use both

Here's the choice in one table. Bash rules Linux servers and containers; PowerShell, Windows and Microsoft 365. Bash's pipe carries text; PowerShell's carries objects. Bash starts in about a millisecond, PowerShell in about a tenth of a second. For JSON, Bash adds jq; PowerShell has it built in. Working mostly on Linux? Bash first. Running Windows machines at work? PowerShell first.

Bash or PowerShell first?: comparison table of Bash vs PowerShell, Where it rules, The pipe carries, One start, In one line

So, to the forum question: for web apps in JavaScript, your code will likely run on Linux servers and in containers, so learn Bash first. Learn PowerShell when your work lives on Windows. The ideas carry over: pipes, variables, scripts. Every command from this video, with the lab files, is linked in the description.

The answer to the forum question: cards of Web apps in JavaScript? Bash first, Work on Windows? PowerShell first, The ideas carry over, Run it yourself

Recap

So: Bash passes text, PowerShell passes objects. Bash starts fast and is on every Linux machine. And in shared scripts, use full PowerShell names, not ls.

If this helped you pick a shell, a like helps YouTube show it to more beginners, and subscribing brings you the next side-by-side test.

Every command and output in this video came from a real run, on Ubuntu 24.04.5 LTS, with Bash 5.2.21 and PowerShell 7.6.9. Thanks for watching Cloud Explainers.

Sources

Demos: a stock Ubuntu 24.04 container on our own lab machine, standing in for a laptop, and a Windows Server 2022 machine in the same lab, reached over SSH; the tea shop, its logs and its orders are made up. Watch the full video on YouTube.

More videos

Docker10 min

What Happens When You Run a Docker Container

Claude & the Claude Developer Platform8 min

How to Fix Claude Code Billing Your API Key Instead of Your Plan

Auth (OAuth, JWT)9 min

Why Passkeys Can't Be Phished Like Passwords

Linux10 min

The Linux Roadmap for Beginners

Django (Python web framework)9 min

Django vs FastAPI: Which One Should You Use?

Databases & SQL10 min

Why SQLite Is the Most Used Database in the World

Databases & SQL11 min

How a NoSQL Database Like MongoDB Works

Python11 min

How to Fix Python pip error: externally-managed-environment

Databases & SQL9 min

How to Fix MySQL Too Many Connections Database Errors

Go (Golang)9 min

Golang Concurrency Will Finally Make Sense After This

AWS10 min

How Amazon S3 Works

Kubernetes10 min

How Kubernetes Works

Git10 min

How Git Works

Docker10 min

How to Fix Docker Port Already Allocated Errors

PostgreSQL10 min

How PostgreSQL Works

Git8 min

How to Fix Git Merge Conflicts

Linux9 min

How to Fix No Space Left on Device in Linux

Networking10 min

How the Internet Works

Databases & SQL9 min

SQL vs NoSQL: When Each One Wins

Auth (OAuth, JWT)8 min

How to Fix JWT Token Expired Errors (401 Unauthorized)

Claude & the Claude Developer Platform9 min

Claude API vs Claude Pro: Why Your Subscription Doesn't Include It

AI agent skills9 min

Why AI Agent Skills Beat Giant Prompts

Linux10 min

Linux Cron Jobs Will Finally Make Sense After This

Cybersecurity10 min

Security Logs Will Finally Make Sense After This

Cybersecurity8 min

How to Fix a Security Leak: An API Key Pushed to GitHub

DNS9 min

DNS Will Finally Make Sense After This

Cybersecurity9 min

Never Ignore a Security Update

Auth (OAuth, JWT)9 min

Two-Factor Authentication Will Finally Make Sense

Networking8 min

Never Expose a Database to the Internet

Cybersecurity10 min

SSH Keys vs Passwords: Which Is Safer for Security?

Linux10 min

The 5 Levels of Linux Boot: From Power On to Login

Networking12 min

The 5 Levels of Network Firewalls

Cybersecurity10 min

Password Security: Why Hackers Crack Weak Passwords

Linux9 min

Why Your Linux Server Runs Out of Memory

Networking10 min

HTTP vs HTTPS: What Actually Changes on Your Network

Linux12 min

Every Linux File Permission You Need to Know

Linux10 min

Linux Processes vs Threads: When Each One Wins

Networking11 min

The OSI Model Will Finally Make Sense After This

Git11 min

The 7 Levels of Git

Networking10 min

Every Network Troubleshooting Command You Need

Cybersecurity10 min

Hashing vs Encryption in Cybersecurity: When Each One Wins

Networking11 min

Why the Internet Ran Out of IP Addresses

Linux11 min

Linux Package Managers: apt vs Snap vs Flatpak

Python9 min

Why Python Is Slow (and When It Doesn't Matter)

Networking11 min

TCP vs UDP: When Each One Wins

Linux8 min

Stop Running Linux as Root (Do This Instead)

Linux11 min

Linux Users and Groups Will Finally Make Sense

Cybersecurity9 min

Why Phishing Beats Good Security (and How to Spot It)

Docker12 min

The 7 Levels of Docker

AI agents & MCP12 min

AI Agents Will Finally Make Sense After This

Git8 min

Stop Using git push --force (Use This Instead)

AI agent skills10 min

Agent Skills vs MCP: When Each One Wins

Networking12 min

Networking Will Finally Make Sense After This

Linux12 min

Every Linux Command You Actually Need

Cybersecurity12 min

The 7 Levels of Cybersecurity

System design12 min

Every System Design Concept You Need, in One Diagram

PostgreSQL10 min

Why PostgreSQL Ignores Your Index