Master the terminal — the most-used interface in DevOps. Every cloud server, Docker container, CI runner, and Kubernetes node runs Linux. Every command shown today includes the Windows equivalent (WSL2, PowerShell & Git Bash).
wsl --install# Check if WSL already installed wsl --status # Single command: installs WSL2 + Ubuntu at once wsl --install # If above fails (older Windows 10), run manually: dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart # Set WSL2 as default wsl --set-default-version 2 # RESTART YOUR PC after the above commands
# After restart: search "Ubuntu" in Start Menu, open it # OR: open Windows Terminal and select Ubuntu from dropdown # Ubuntu finishes setup then prompts: Enter new UNIX username: bastian (lowercase, no spaces) New password: *** (won't show while typing) Retype new password: *** # Verify you are inside Linux: uname -a # Linux DESKTOP-XXXX 5.15.0-microsoft-standard-WSL2 ... GNU/Linux cat /etc/os-release | grep PRETTY_NAME # PRETTY_NAME="Ubuntu 22.04.3 LTS"
# Inside Ubuntu terminal sudo apt update && sudo apt upgrade -y sudo apt install -y \ curl wget git vim nano \ htop tree unzip zip \ net-tools iputils-ping \ build-essential lsb-release # Node.js via nvm (recommended over apt) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc nvm install --lts node --version # v20.x.x npm --version # 10.x.x git --version # git version 2.34.x
# Inside WSL2 terminal: open VS Code code . # VS Code opens, bottom-left shows ">< WSL: Ubuntu" # All editing now happens inside Linux filesystem # Docker Desktop auto-integrates with WSL2 docker --version docker run hello-world # Running from inside WSL2! # Access Windows drive from WSL2: ls /mnt/c/Users/YourName/Documents # Access WSL2 files from Windows Explorer: # Address bar: \wsl$\Ubuntu\home\yourname
winver| Purpose | Linux / Mac / WSL2 | Windows PowerShell | Git Bash |
|---|---|---|---|
| Current directory | pwd | Get-Location (pwd) | pwd |
| List files (detailed) | ls -la | Get-ChildItem (ls) | ls -la |
| Change directory | cd /path/to/dir | Set-Location C:\path | cd /path/to/dir |
| Go home | cd ~ or cd | cd ~ | cd ~ |
| Create directory | mkdir -p a/b/c | New-Item -ItemType Dir a\b\c | mkdir -p a/b/c |
| Create empty file | touch file.txt | New-Item file.txt | touch file.txt |
| Copy file | cp src dest | Copy-Item src dest | cp src dest |
| Move / Rename | mv old new | Move-Item old new | mv old new |
| Delete file | rm file.txt | Remove-Item file.txt | rm file.txt |
| Delete directory | rm -rf dir/ | Remove-Item -Recurse dir\ | rm -rf dir/ |
| Find files | find . -name "*.yml" | Get-ChildItem -Recurse -Filter "*.yml" | find . -name "*.yml" |
| View file | cat file.txt | Get-Content file.txt | cat file.txt |
| Clear screen | clear (Ctrl+L) | Clear-Host (cls) | clear |
# Basic search
grep "ERROR" /var/log/app.log
# Case-insensitive
grep -i "error" app.log
# Show line numbers
grep -n "WARN" app.log
# Recursive search in all .log files
grep -r "OutOfMemory" /var/log/
# Context: 3 lines before and after
grep -B3 -A3 "FATAL" app.log
# Count matches
grep -c "ERROR" app.log
# Exclude lines (inverse)
grep -v "DEBUG" app.log
# Live log follow + filter (ESSENTIAL!)
tail -f app.log | grep "ERROR"
# Last 20 lines
tail -20 app.log
# First 10 lines
head -10 app.log
# Count lines in a file
wc -l app.log
# Combine with pipes
cat app.log | grep "ERROR" | wc -l
# Replace text
sed 's/localhost/prod-db/g' config.yml
# Extract column (3rd field)
awk '{print $3}' access.log
# Basic search
Select-String "ERROR" app.log
# Case-insensitive (default in PS)
Select-String "error" app.log
# Show line numbers (default in PS)
Select-String "WARN" app.log
# Recursive search
Get-ChildItem -Recurse *.log |
Select-String "OutOfMemory"
# Context lines
Select-String "FATAL" app.log -Context 3,3
# Count matches
(Select-String "ERROR" app.log).Count
# Exclude lines
Get-Content app.log |
Where-Object {$_ -notmatch "DEBUG"}
# Live follow + filter
Get-Content -Wait app.log |
Where-Object { $_ -match "ERROR" }
# Last 20 lines
Get-Content app.log | Select -Last 20
# First 10 lines
Get-Content app.log | Select -First 10
# Count lines
(Get-Content app.log).Count
# Pipe + count
(Select-String "ERROR" app.log).Count
# Replace text
(Get-Content config.yml) -replace
'localhost','prod-db' |
Set-Content config.yml
| Purpose | Linux / Mac / WSL2 | Windows PowerShell |
|---|---|---|
| List processes | ps aux | Get-Process |
| Live CPU/memory monitor | top (or htop) | Get-Process | Sort CPU -Desc |
| Kill process by PID | kill -9 <PID> | Stop-Process -Id <PID> |
| Service status | systemctl status nginx | Get-Service nginx |
| Start service | systemctl start nginx | Start-Service nginx |
| Open listening ports | ss -tlnp | Get-NetTCPConnection -State Listen |
| HTTP request | curl -I https://example.com | Invoke-WebRequest https://example.com |
| DNS lookup | dig example.com | Resolve-DnsName example.com |
| Ping host | ping -c 4 google.com | Test-Connection google.com |
| Disk usage | df -h | Get-PSDrive |
| Memory usage | free -h | Get-CimInstance Win32_OperatingSystem |
| Check if port open | nc -zv host 443 | Test-NetConnection host -Port 443 |
-rwxr-xr--# View permissions ls -la script.sh # Make script executable (most common task) chmod +x script.sh chmod 755 script.sh # same result # Config files (owner read/write only) chmod 600 ~/.ssh/id_rsa chmod 644 /etc/nginx/nginx.conf # Change ownership chown user:group file.txt chown -R ubuntu:ubuntu /opt/myapp/ # Run as superuser sudo systemctl restart nginx sudo chmod 755 /opt/app whoami # who am I? id # show full user + group IDs
# View file permissions (ACL) Get-Acl script.ps1 | Format-List # Set file read-only Set-ItemProperty file.txt -Name IsReadOnly $true # Run as Administrator = Linux sudo # Right-click PowerShell > "Run as Administrator" # Check current user whoami # works in PS too! # Set execution policy (run scripts) Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
# View all env variables env printenv PATH # Set temp variable (current session only) export DB_PASSWORD="mypassword" export APP_ENV="staging" # Use variables echo $DB_PASSWORD echo "Running in: $APP_ENV" # Make permanent (add to ~/.bashrc or ~/.zshrc) echo 'export APP_ENV="production"' >> ~/.bashrc source ~/.bashrc # reload without restart # Add to PATH permanently echo 'export PATH="$PATH:/opt/myapp/bin"' >> ~/.bashrc # Unset a variable unset DB_PASSWORD # Check if variable is set if [ -z "$MY_VAR" ]; then echo "MY_VAR is not set!" fi
# View all env variables
Get-ChildItem Env:
$Env:PATH
# Set temp variable (current session)
$Env:DB_PASSWORD = "mypassword"
$Env:APP_ENV = "staging"
# Use variables
Write-Host $Env:DB_PASSWORD
Write-Host "Running in: $Env:APP_ENV"
# Permanent (User scope)
[System.Environment]::SetEnvironmentVariable(
"APP_ENV", "production", "User")
# Add to PATH (User scope)
$p = [System.Environment]::GetEnvironmentVariable("PATH","User")
[System.Environment]::SetEnvironmentVariable(
"PATH", "$p;C:\opt\myapp\bin", "User")
# Unset
Remove-Item Env:DB_PASSWORD
# Check if set
if (-not $Env:MY_VAR) {
Write-Host "MY_VAR is not set!"
}
export DB_PASS="prod123" in a git-committed script = immediate security vulnerability.
#!/bin/bash
# Shebang: tells OS to use bash interpreter
# Variables
APP_NAME="devops-app"
VERSION="1.0.0"
echo "Deploying $APP_NAME v$VERSION"
# Command substitution
DATE=$(date +%Y-%m-%d)
HOST=$(hostname)
echo "Host: $HOST | Date: $DATE"
# Conditionals
if [ "$APP_ENV" == "production" ]; then
echo "Production deployment"
elif [ "$APP_ENV" == "staging" ]; then
echo "Staging deployment"
else
echo "Unknown environment"; exit 1
fi
# Loops over array
SERVICES=("nginx" "redis" "postgres")
for SERVICE in "${SERVICES[@]}"; do
systemctl is-active --quiet $SERVICE \
&& echo "OK: $SERVICE" \
|| echo "DOWN: $SERVICE"
done
# Function with argument
check_port() {
nc -z localhost $1 2>/dev/null \
&& echo "Port $1: OPEN" \
|| echo "Port $1: CLOSED"
}
check_port 80
check_port 8080
# Variables
$AppName = "devops-app"
$Version = "1.0.0"
Write-Host "Deploying $AppName v$Version"
# Command substitution
$Date = Get-Date -Format "yyyy-MM-dd"
$HostName = $env:COMPUTERNAME
Write-Host "Host: $HostName | Date: $Date"
# Conditionals
$AppEnv = $env:APP_ENV
if ($AppEnv -eq "production") {
Write-Host "Production deployment"
} elseif ($AppEnv -eq "staging") {
Write-Host "Staging deployment"
} else {
Write-Host "Unknown environment"
exit 1
}
# Loops over array
$Services = @("nginx","redis","postgres")
foreach ($svc in $Services) {
$s = Get-Service $svc -ErrorAction SilentlyContinue
if ($s -and $s.Status -eq "Running") {
Write-Host "OK: $svc" -ForegroundColor Green
} else {
Write-Host "DOWN: $svc" -ForegroundColor Red
}
}
# Function
function Test-Port($Port) {
$c = Test-NetConnection localhost -Port $Port -WA SilentlyContinue
Write-Host "Port $Port`: $(if($c.TcpTestSucceeded){'OPEN'}else{'CLOSED'})"
}
Test-Port 80
Test-Port 8080
Write bash + PowerShell scripts that report system info — runs on Linux, Mac, WSL2 & Windows
cd devops-35days-labs && mkdir -p scripts && cd scriptschmod +x sysinfo.sh. Run: ./sysinfo.sh.\sysinfo.ps1 (If blocked: Set-ExecutionPolicy RemoteSigned -Scope CurrentUser)grep "ERROR" sample-app.log | wc -lgit add . && git commit -m "feat(day4): sysinfo scripts bash+ps1" && git push#!/bin/bash
echo "=================================================="
echo " SYSTEM INFO REPORT"
echo " Generated: $(date)"
echo "=================================================="
echo ""
echo "--- HOST ---"
echo "Hostname : $(hostname)"
echo "OS : $(uname -s) $(uname -r)"
echo "Arch : $(uname -m)"
[ -f /etc/os-release ] && . /etc/os-release && echo "Distro : $NAME $VERSION_ID"
echo ""
echo "--- USER ---"
echo "User : $(whoami)"
echo "Home : $HOME"
echo "Shell : $SHELL"
echo ""
echo "--- RESOURCES ---"
CPU_CORES=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo "N/A")
echo "CPU Cores : $CPU_CORES"
command -v free &>/dev/null && \
free -h | awk '/^Mem:/{printf "Memory : %s total, %s used\n",$2,$3}'
df -h / | awk 'NR==2{printf "Disk (/) : %s used of %s (%s)\n",$3,$2,$5}'
echo ""
echo "--- NETWORK ---"
ip addr show 2>/dev/null | grep 'inet ' | grep -v '127.0.0.1' | \
awk '{print "IP : " $2}' | head -1
curl -s --max-time 3 -o /dev/null -w "Internet : HTTP %{http_code}\n" http://google.com
echo ""
echo "--- DEVOPS TOOLS INSTALLED ---"
for TOOL in git docker kubectl helm terraform ansible node python3; do
if command -v $TOOL &> /dev/null; then
VER=$($TOOL --version 2>&1 | head -1)
printf " OK %-12s %s\n" "$TOOL" "$VER"
else
printf " -- %-12s not installed\n" "$TOOL"
fi
done
echo ""
echo "=================================================="
# To run: chmod +x sysinfo.sh && ./sysinfo.sh
Write-Host "==================================================" -ForegroundColor Cyan
Write-Host " SYSTEM INFO REPORT $(Get-Date)" -ForegroundColor White
Write-Host "==================================================" -ForegroundColor Cyan
Write-Host "`n--- HOST ---" -ForegroundColor Yellow
Write-Host "Hostname : $env:COMPUTERNAME"
$os = Get-CimInstance Win32_OperatingSystem
Write-Host "OS : $($os.Caption) $($os.Version)"
Write-Host "Arch : $env:PROCESSOR_ARCHITECTURE"
Write-Host "`n--- USER ---" -ForegroundColor Yellow
Write-Host "User : $env:USERNAME"
Write-Host "Home : $env:USERPROFILE"
Write-Host "`n--- RESOURCES ---" -ForegroundColor Yellow
$cpu = Get-CimInstance Win32_Processor | Select -First 1
Write-Host "CPU : $($cpu.Name) ($($cpu.NumberOfCores) cores)"
$totalGB = [math]::Round($os.TotalVisibleMemorySize/1MB,1)
$freeGB = [math]::Round($os.FreePhysicalMemory/1MB,1)
$usedGB = $totalGB - $freeGB
Write-Host "Memory : ${usedGB}GB used / ${totalGB}GB total"
Get-PSDrive -PSProvider FileSystem | Where {$_.Used -gt 0} | ForEach {
$u = [math]::Round($_.Used/1GB,1); $f = [math]::Round($_.Free/1GB,1)
Write-Host "Disk $($_.Name): ${u}GB used, ${f}GB free"
}
Write-Host "`n--- NETWORK ---" -ForegroundColor Yellow
$ip = (Get-NetIPAddress -AddressFamily IPv4 |
Where {$_.IPAddress -ne "127.0.0.1"} | Select -First 1).IPAddress
Write-Host "IP : $ip"
$ping = Test-Connection google.com -Count 1 -Quiet -ErrorAction SilentlyContinue
Write-Host "Internet : $(if($ping){'Connected'}else{'No connection'})"
Write-Host "`n--- DEVOPS TOOLS ---" -ForegroundColor Yellow
@("git","docker","kubectl","helm","terraform","node","python") | ForEach {
if (Get-Command $_ -ErrorAction SilentlyContinue) {
$v = (& $_ --version 2>&1 | Select -First 1)
Write-Host " OK $_`: $v" -ForegroundColor Green
} else {
Write-Host " -- $_`: not installed" -ForegroundColor DarkGray
}
}
Write-Host "`n==================================================" -ForegroundColor Cyan
# Run: .\sysinfo.ps1
# If blocked: Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
# Create sample log cat > /tmp/app.log << 'EOF' 2024-01-15 08:01 INFO App started on port 3000 2024-01-15 08:05 WARN Response time > 500ms 2024-01-15 08:07 ERROR Cache timeout on redis:6379 2024-01-15 08:15 ERROR NullPointerException in UserService 2024-01-15 08:22 WARN Memory usage at 78% 2024-01-15 09:00 ERROR DB connection pool exhausted EOF # Try these: grep "ERROR" /tmp/app.log grep -c "ERROR" /tmp/app.log grep -n "WARN\|ERROR" /tmp/app.log grep -v "INFO" /tmp/app.log tail -3 /tmp/app.log grep "ERROR" /tmp/app.log | wc -l # Real system logs sudo tail -20 /var/log/syslog journalctl -n 30 --no-pager
# Create sample log
@"
2024-01-15 08:01 INFO App started on port 3000
2024-01-15 08:05 WARN Response time > 500ms
2024-01-15 08:07 ERROR Cache timeout on redis:6379
2024-01-15 08:15 ERROR NullPointerException in UserService
2024-01-15 08:22 WARN Memory usage at 78%
2024-01-15 09:00 ERROR DB connection pool exhausted
"@ | Set-Content "$env:TEMP\app.log"
# Try these:
Select-String "ERROR" "$env:TEMP\app.log"
(Select-String "ERROR" "$env:TEMP\app.log").Count
Select-String "WARN|ERROR" "$env:TEMP\app.log"
Get-Content "$env:TEMP\app.log" | Where {$_ -notmatch "INFO"}
Get-Content "$env:TEMP\app.log" | Select -Last 3
# Windows Event Log
Get-EventLog Application -EntryType Error -Newest 10
3 questions · 5 minutes · Linux + Windows commands
grep "ERROR" app.log | wc -lchmod 755 sysinfo.sh && ./sysinfo.sh| Task | Linux / Mac / WSL2 | PowerShell | Git Bash |
|---|---|---|---|
| Working dir | pwd | Get-Location | pwd |
| List files | ls -la | Get-ChildItem | ls -la |
| View file | cat file | Get-Content file | cat file |
| Search text | grep "text" file | Select-String "text" file | grep "text" file |
| Live log | tail -f app.log | Get-Content -Wait app.log | tail -f app.log |
| Process list | ps aux | Get-Process | ps aux |
| Kill process | kill -9 PID | Stop-Process -Id PID | kill -9 PID |
| Disk usage | df -h | Get-PSDrive | df -h |
| HTTP request | curl URL | Invoke-WebRequest URL | curl URL |
| DNS lookup | dig domain | Resolve-DnsName domain | nslookup domain |
| Permissions | chmod +x file | Get-Acl / Set-Acl | chmod +x file |
| Current user | whoami | whoami | whoami |
| Env variable | export VAR=val | $Env:VAR = "val" | export VAR=val |
| Run as admin | sudo command | Run as Administrator | sudo (WSL) |