What a script is

A script is a program that an interpreter runs straight from its source text, without a separate compile step. A shell script is the simplest kind: the same commands you type at the prompt, written in a file and run from top to bottom (see Shell Scripting Basics). Shell languages are scripting languages built for one job above all: starting other programs and gluing their results together.

  • A Bash script is a text file of Bash commands, usually named something.sh and started with a shebang line such as #!/bin/bash, so that Linux knows which program must run it.
  • A PowerShell script is a text file of PowerShell commands with the extension .ps1. The extension matters: inside PowerShell only a .ps1 file runs as a script, and on Windows pwsh -File also refuses any other extension.

Here is the same small check in both languages: warn when a disk is above 90% full. The Bash version comes first on every page of this topic, the PowerShell version directly after it.

usage=91
threshold=90
if (( usage > threshold )); then
  echo "WARNING: disk at ${usage}%"
fi
WARNING: disk at 91%
$usage = 91
$threshold = 90
if ($usage -gt $threshold) {
  "WARNING: disk at $usage%"
}
WARNING: disk at 91%

The shape is the same; the details are not. PowerShell variables always start with $, also when you assign them, and it compares numbers with -gt (greater than). A string on its own line, such as "WARNING: …", is simply output: PowerShell needs no echo. In Bash, (( … )) is arithmetic, where > means greater than.

$usage = 91
if ($usage > 90) {
  "WARNING: disk at $usage%"
}
"Files in this folder now:"
Get-ChildItem -Name
Get-Content ./90
Files in this folder now:
90
example.ps1
91

No warning, a stray file named 90 that contains 91, and no error message. Use -gt, -lt, -eq and their relatives, explained on Conditions, Tests and Exit Codes.

When a script pays off

A script costs time to write, test and maintain. It earns that time back in four situations, and the service desk and operations work at Kestrel Energy, the fictional Dutch energy supplier used as an example throughout this site, is full of them:

  • You repeat it. Amara Okafor on the service desk checks the same three things for every "portal is slow" ticket. Typed, that is five minutes and a chance of skipping a step; scripted, it is one command whose output goes straight into the ticket.
  • It must be identical every time. Creating an account, rotating a log or preparing a server should not depend on who does it or how tired they are. A script is a procedure that cannot skip a line.
  • It has to run without you. A nightly backup check or a disk alert runs at 03:00 from a scheduler (see Scheduling and Logging), which only works if every step is written down as code.
  • It runs on many machines. Tomasz Nowak in the network operations center checks a setting on forty servers; one loop over a list of host names beats forty remote sessions (see Remote Execution).

A rough rule: if you have done a task by hand three times and expect to do it again, script it. Start small, by pasting the commands you used into a file, and improve the script each time you use it.

When to choose something else

Shell scripts are glue. They are excellent at running commands, checking results and moving files, and poor at complex logic, large data structures and calculations. Google's Shell Style Guide puts the limit bluntly: a shell script of more than 100 lines, or one with control flow that is not straightforward, should be rewritten in a more structured language. For Bash, that usually means Python (see the Python topic). PowerShell, with real objects and the whole .NET library behind it, stretches further before that point, but the advice is the same: when a script becomes an application, treat it like one.

And some work is better done with a purpose-built tool: a configuration management tool such as Ansible for keeping many servers in a known state, a CI/CD (continuous integration and continuous delivery) system for builds and deployments. Those tools still run small scripts at their edges, which is one more reason to write good ones.

Shells, interpreters and script hosts

A shell is the program that reads your commands and runs them. It works in two modes.

Interactive and non-interactive

An interactive shell is a shell that reads commands from a person at a terminal: it shows a prompt, keeps a history, completes names with Tab, and loads your personal settings, such as aliases in ~/.bashrc or your PowerShell profile. A shell that runs a script is non-interactive: no prompt, no history, and in Bash no ~/.bashrc. So a command that works at your prompt can fail in a script, because it relied on something only your interactive session had:

cat > report.sh <<'X'
#!/bin/bash
alias ll='ls -l'
ll
X
bash report.sh
echo "exit status: $?"
report.sh: line 3: ll: command not found
exit status: 127

Bash does not expand aliases in a non-interactive shell, so ll is an unknown command (status 127). Scripts should use full command names, and they should not depend on your personal settings at all: a scheduler runs them as another user, with another environment. PowerShell's equivalent advice is to write full cmdlet names (Get-ChildItem, not gci) in scripts, and to start scheduled scripts with pwsh -NoProfile so that nobody's profile can change what they do.

The script host

The script host is the program that reads a script file and runs it: bash for a Bash script, pwsh (PowerShell 7) or powershell.exe (Windows PowerShell 5.1) for a .ps1 file. Both are interpreters: they read and execute the script line by line, which is why a typo on line 40 shows up only after lines 1 to 39 have already run. The shebang line, the execute permission and PowerShell's execution policy decide how the right host gets started; that is the subject of Running Scripts.

Bash and PowerShell compared

Bash (the GNU Bourne-Again SHell) is the standard shell of most Linux distributions and the language of countless installation scripts, container entry points and cron jobs. PowerShell is Microsoft's shell and scripting language, built on .NET; it is the standard tool for administering Windows, Active Directory, Microsoft 365 and Azure, and since PowerShell 7 it also runs on Linux and macOS.

Text versus objects

The deepest difference is what travels between commands. Bash connects separate programs with pipes, and what flows through a pipe is text: lines of characters that the next program has to cut apart again. PowerShell's cmdlets run inside one process and hand each other .NET objects with named properties (see Objects, not text). The same question, "which meter used the most energy?", answered both ways:

cat > readings.csv <<'CSV'
meter,kwh
M-1042,4521
M-1043,312
M-1044,2870
CSV

tail -n +2 readings.csv | sort -t, -k2 -n | tail -n 1
M-1042,4521
@'
meter,kwh
M-1042,4521
M-1043,312
M-1044,2870
'@ | Set-Content readings.csv

$rows = Import-Csv readings.csv
$top = $rows | Sort-Object { [int]$_.kwh } | Select-Object -Last 1
"$($top.meter) used $($top.kwh) kWh"
$rows[0].GetType().Name
M-1042 used 4521 kWh
PSCustomObject

The Bash pipeline works on positions in the text: skip line 1 (the header), split at commas, sort numerically on field 2, keep the last line. Change the column order in the file and the command silently sorts the wrong thing. The PowerShell pipeline works on names: each row is an object with the properties meter and kwh, taken from the header. The price is more typing, and one catch: values read from a CSV file are strings, so the script converts them with [int] to sort them as numbers.

In Bash, sort receives lines of text such as M-1042,4521 and must be told which field to use. In PowerShell, Sort-Object receives objects with the properties meter and kwh and sorts by property name. Bash: text tail -n +2 sort -t, -k2 -n tail -n 1 lines of characters: "M-1043,312" fields found by position PowerShell: objects Import-Csv Sort-Object kwh Select-Object objects: meter = "M-1043", kwh = "312" fields found by name
The same three steps. Between Bash commands travels text that each program parses again; between cmdlets travel objects whose properties keep their names.

The Pipeline: Text versus Objects works this difference out in detail.

The main differences at a glance

Bash PowerShell
Home ground Linux, macOS, containers, CI jobs Windows, Active Directory, Microsoft 365, Azure; also Linux and macOS
Between commands Text (bytes and lines) .NET objects with properties
Commands Separate programs (grep, sort) plus builtins Cmdlets named Verb-Noun, plus native programs
Variables name=value, read as $name; everything is a string $name = value; typed values (numbers, dates, objects)
Comparison [[ $a -gt 5 ]], (( a > 5 )) $a -gt 5
Case Case-sensitive everywhere Commands, variables and -eq are case-insensitive
Errors Exit status per command (0 = success) Error records; terminating and non-terminating errors
Script file .sh by convention, shebang, execute permission .ps1 required, execution policy on Windows
Help man bash, help read Get-Help about_Quoting_Rules

Which one to learn first

Learn the one your systems speak. Linux servers, containers and most CI pipelines expect Bash (or the plainer POSIX sh); Windows desktops and servers, Active Directory, Exchange and Microsoft 365 are administered with PowerShell, and many of their management modules exist only for PowerShell. In mixed environments you will read both, which is exactly what the side-by-side layout of this topic is for. PowerShell 7 runs fine on Linux and Bash runs on Windows through WSL (Windows Subsystem for Linux), but scripts are happiest on their home ground; see Cross-Platform Scripting.

Versions and support

Both languages are actively maintained, and which version you have matters: features and defaults change between releases. The state as of October 2026:

Version Status (October 2026)
Bash 5.3 Current release (5 July 2025). Adds, among other things, command substitution without a subshell, ${ command; }.
Bash 5.2 What most Linux servers run: Debian 12 ships 5.2.15 and Debian 13 ships 5.2.37. The examples on these pages run on Bash 5.2 and avoid 5.3-only features.
PowerShell 7.6 (LTS) Long Term Servicing release, released 18 March 2026, supported until 14 November 2028; built on .NET 10. The PowerShell examples on these pages run on 7.6.6.
PowerShell 7.5 and 7.4 (LTS) Both reach end of support on 10 November 2026: upgrade to 7.6.
PowerShell 7.7 In preview; not for production.
Windows PowerShell 5.1 Part of Windows and supported as long as the Windows version it ships with; it receives security fixes but no new features.
Windows PowerShell 2.0 Removed from Windows 11 version 24H2 and Windows Server 2025 (August and September 2025). Scripts that call powershell.exe -Version 2 no longer get it.

Windows PowerShell 5.1 (powershell.exe) and PowerShell 7 (pwsh.exe) are separate products that install side by side: installing 7 does not replace 5.1. Most scripts run in both, but there are differences, and some old Windows-only modules work only in 5.1. The PowerShell ISE (Integrated Scripting Environment) is still included with Windows but no longer actively developed; Microsoft recommends Visual Studio Code with the PowerShell extension.

Check which version a machine has before you rely on a feature:

echo "Bash major version: ${BASH_VERSINFO[0]}"
Bash major version: 5
"Edition: $($PSVersionTable.PSEdition)"
"Major version: $($PSVersionTable.PSVersion.Major)"
Edition: Core
Major version: 7

PSEdition is Core for PowerShell 7 and Desktop for Windows PowerShell 5.1. At a prompt, bash --version and $PSVersionTable show the full details:

bash --version | head -n 1
GNU bash, version 5.2.15(1)-release (aarch64-unknown-linux-gnu)

How this topic works

This topic is about writing scripts, not about the commands themselves. The commands are explained elsewhere, and these pages link there instead of repeating them:

On every page you will find the same conventions:

  • Bash first, PowerShell second, solving the same task, with a sentence on what differs and why. Most pages end a section with a small comparison table.
  • Examples with output are run. Every Bash example runs in an empty folder with Bash 5.2, every PowerShell example with PowerShell 7.6 on Linux, and the output shown is what they really print. Error messages show the script folder as /home/amara/scripts. PowerShell on Windows behaves the same for everything shown in runnable examples.
  • Some examples cannot run here: anything that needs a network, another server, administrator rights or Windows-only cmdlets. Those are shown as plain code blocks, with output labeled "illustrative".
  • Kestrel Energy supplies the data: meter readings in kWh, outage tickets such as INC-20419, servers such as web01 and db01 on documentation IP addresses (192.0.2.x), log files and CSV exports.