Skip to content

Instantly share code, notes, and snippets.

@mosioc
Created December 29, 2025 17:00
Show Gist options
  • Select an option

  • Save mosioc/85ca27636653e9270f3086ea60a3b30a to your computer and use it in GitHub Desktop.

Select an option

Save mosioc/85ca27636653e9270f3086ea60a3b30a to your computer and use it in GitHub Desktop.
Shell scripting in Bash, Node.js, and Python cheat sheet

Shell Scripting Cheat Sheet: Bash, Node.js, and Python

Quick reference for writing scripts in Bash, Node.js, and Python with practical examples and explanations.


Core Concepts

Script Anatomy

  • Shebang: First line that tells the system which interpreter to use
  • Permissions: Scripts need execute permission (chmod +x script)
  • Exit codes: 0 = success, non-zero = error
  • Arguments: Pass data to scripts via command line

When to Use Which

  • Bash: System automation, file operations, command orchestration
  • Node.js: Async I/O, API interactions, modern JS features, npm ecosystem
  • Python: Data processing, complex logic, readability, scientific computing
  1. Bash Scripting
  2. Node.js Scripting
  3. Python Scripting

Bash Scripting

Shebang and Basics

bash

#!/bin/bash
# This is a comment

# Make executable: chmod +x script.sh
# Run: ./script.sh

Variables

bash

# Assignment (no spaces around =)
name="John"
count=42

# Access with $
echo "Hello $name"
echo "Count: ${count}"

# Command substitution
current_date=$(date)
files=`ls`  # Old style, prefer $()

# Environment variables
export PATH="/usr/local/bin:$PATH"

Explanation: Bash variables don't have explicit types. Use ${} for clarity and to avoid ambiguity. Command substitution captures output of commands.

Arguments and Special Variables

bash

# $0 = script name
# $1, $2... = positional arguments
# $# = number of arguments
# $@ = all arguments as separate words
# $* = all arguments as single word
# $? = exit code of last command

echo "Script: $0"
echo "First arg: $1"
echo "All args: $@"
echo "Arg count: $#"

Conditionals

bash

# If statement
if [ "$name" = "John" ]; then
    echo "Hello John"
elif [ "$name" = "Jane" ]; then
    echo "Hello Jane"
else
    echo "Hello stranger"
fi

# File tests
if [ -f "file.txt" ]; then
    echo "File exists"
fi

# Numeric comparison
if [ $count -gt 10 ]; then
    echo "Greater than 10"
fi

# Modern syntax (recommended)
if [[ $name == "John" ]]; then
    echo "Using double brackets"
fi

Explanation: Use [[ ]] over [ ] for better error handling and more features. Numeric comparisons use -eq, -ne, -gt, -lt, -ge, -le. String comparisons use =, !=, <, >.

Loops

bash

# For loop
for i in 1 2 3 4 5; do
    echo "Number: $i"
done

# C-style for loop
for ((i=0; i<5; i++)); do
    echo $i
done

# While loop
while [ $count -gt 0 ]; do
    echo $count
    ((count--))
done

# Loop over files
for file in *.txt; do
    echo "Processing $file"
done

# Loop over command output
for line in $(cat file.txt); do
    echo $line
done

Functions

bash

# Define function
greet() {
    local name=$1  # Local variable
    echo "Hello $name"
    return 0  # Return exit code (0-255)
}

# Call function
greet "Alice"

# Capture return value
greet "Bob"
result=$?

Explanation: Functions use positional parameters like scripts. Use local to avoid polluting global scope. return sets exit code, not return value.

String Operations

bash

string="Hello World"

# Length
echo ${#string}  # 11

# Substring
echo ${string:0:5}  # "Hello"

# Replace
echo ${string/World/Universe}  # "Hello Universe"

# Upper/lowercase
echo ${string^^}  # HELLO WORLD
echo ${string,,}  # hello world

Arrays

bash

# Define array
fruits=("apple" "banana" "cherry")

# Access elements
echo ${fruits[0]}  # apple

# All elements
echo ${fruits[@]}

# Array length
echo ${#fruits[@]}

# Loop over array
for fruit in "${fruits[@]}"; do
    echo $fruit
done

Input/Output

bash

# Read input
read -p "Enter name: " name
echo "You entered: $name"

# Read from file
while IFS= read -r line; do
    echo $line
done < input.txt

# Redirect output
echo "text" > file.txt   # Overwrite
echo "more" >> file.txt  # Append

# Redirect stderr
command 2> error.log
command &> all.log  # stdout and stderr

Error Handling

bash

# Exit on error
set -e

# Exit on undefined variable
set -u

# Pipe failure detection
set -o pipefail

# Combined
set -euo pipefail

# Manual error check
if ! command; then
    echo "Command failed"
    exit 1
fi

Explanation: set -e stops script on any error. set -u catches typos in variable names. set -o pipefail makes pipes fail if any command fails.


Node.js Scripting

Shebang and Setup

javascript

#!/usr/bin/env node

// Make executable: chmod +x script.js
// Run: ./script.js or node script.js

console.log('Hello from Node.js');

Arguments

javascript

// process.argv[0] = node path
// process.argv[1] = script path
// process.argv[2+] = actual arguments

const args = process.argv.slice(2);
console.log('Arguments:', args);

// Using minimist (npm install minimist)
const minimist = require('minimist');
const argv = minimist(process.argv.slice(2));
console.log(argv);
// node script.js --name John --age 30
// { _: [], name: 'John', age: 30 }

Explanation: process.argv includes node and script paths. Use libraries like minimist, yargs, or commander for complex argument parsing.

Environment Variables

javascript

// Access
const dbHost = process.env.DB_HOST || 'localhost';

// Set (for child processes)
process.env.MY_VAR = 'value';

// Using dotenv (npm install dotenv)
require('dotenv').config();
console.log(process.env.API_KEY);

File System Operations

javascript

const fs = require('fs');
const path = require('path');

// Read file (sync)
const content = fs.readFileSync('file.txt', 'utf8');

// Read file (async)
fs.readFile('file.txt', 'utf8', (err, data) => {
    if (err) throw err;
    console.log(data);
});

// Promises API
const fsPromises = require('fs').promises;
async function readFileAsync() {
    const data = await fsPromises.readFile('file.txt', 'utf8');
    return data;
}

// Write file
fs.writeFileSync('output.txt', 'content');

// Check existence
if (fs.existsSync('file.txt')) {
    console.log('File exists');
}

// Directory operations
fs.readdirSync('.').forEach(file => {
    console.log(file);
});

Explanation: Use async operations for I/O to avoid blocking. Modern Node.js supports fs.promises for cleaner async/await syntax.

Path Operations

javascript

const path = require('path');

// Join paths
const fullPath = path.join(__dirname, 'data', 'file.txt');

// Get filename
const filename = path.basename('/path/to/file.txt');  // 'file.txt'

// Get extension
const ext = path.extname('file.txt');  // '.txt'

// Get directory
const dir = path.dirname('/path/to/file.txt');  // '/path/to'

// Resolve absolute path
const absolute = path.resolve('relative/path');

Running Shell Commands

javascript

const { exec, execSync, spawn } = require('child_process');

// Synchronous (blocking)
const output = execSync('ls -la', { encoding: 'utf8' });
console.log(output);

// Asynchronous with callback
exec('ls -la', (error, stdout, stderr) => {
    if (error) {
        console.error(`Error: ${error}`);
        return;
    }
    console.log(stdout);
});

// Spawn for streaming output
const child = spawn('ping', ['google.com']);
child.stdout.on('data', (data) => {
    console.log(`Output: ${data}`);
});
child.on('close', (code) => {
    console.log(`Exited with code ${code}`);
});

Explanation: exec buffers output, spawn streams it. Use spawn for long-running processes or large outputs.

Error Handling and Exit

javascript

// Exit codes
process.exit(0);  // Success
process.exit(1);  // Error

// Uncaught exceptions
process.on('uncaughtException', (err) => {
    console.error('Uncaught exception:', err);
    process.exit(1);
});

// Unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
    console.error('Unhandled rejection:', reason);
    process.exit(1);
});

// Try-catch
try {
    const data = JSON.parse(invalidJson);
} catch (error) {
    console.error('Parse error:', error.message);
    process.exit(1);
}

Async Patterns

javascript

// Promises
function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// Async/await
async function main() {
    console.log('Start');
    await delay(1000);
    console.log('After 1 second');
}

// Parallel execution
async function fetchAll() {
    const [result1, result2] = await Promise.all([
        fetch('url1'),
        fetch('url2')
    ]);
    return [result1, result2];
}

// Error handling with async/await
async function safeOperation() {
    try {
        const result = await riskyOperation();
        return result;
    } catch (error) {
        console.error('Operation failed:', error);
        throw error;
    }
}

User Input

javascript

const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

rl.question('What is your name? ', (answer) => {
    console.log(`Hello ${answer}`);
    rl.close();
});

// Promise-based
function askQuestion(query) {
    const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });
    
    return new Promise(resolve => rl.question(query, ans => {
        rl.close();
        resolve(ans);
    }));
}

async function main() {
    const name = await askQuestion('Name? ');
    console.log(`Hello ${name}`);
}

Python Scripting

Shebang and Basics

python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Make executable: chmod +x script.py
# Run: ./script.py or python3 script.py

print("Hello from Python")

Arguments

python

import sys

# sys.argv[0] = script name
# sys.argv[1+] = arguments
print(f"Script: {sys.argv[0]}")
print(f"Arguments: {sys.argv[1:]}")

# Using argparse (recommended)
import argparse

parser = argparse.ArgumentParser(description='Process some data')
parser.add_argument('input', help='Input file')
parser.add_argument('--output', '-o', default='out.txt', help='Output file')
parser.add_argument('--verbose', '-v', action='store_true', help='Verbose mode')
parser.add_argument('--count', type=int, default=1, help='Number of iterations')

args = parser.parse_args()
print(f"Input: {args.input}")
print(f"Output: {args.output}")
print(f"Verbose: {args.verbose}")

Explanation: argparse provides automatic help generation, type conversion, and validation. Use it for any script with more than one or two arguments.

Environment Variables

python

import os

# Access
db_host = os.environ.get('DB_HOST', 'localhost')
api_key = os.getenv('API_KEY')

# Set
os.environ['MY_VAR'] = 'value'

# Check existence
if 'PATH' in os.environ:
    print(os.environ['PATH'])

# Load from .env file (pip install python-dotenv)
from dotenv import load_dotenv
load_dotenv()

File Operations

python

# Read entire file
with open('file.txt', 'r') as f:
    content = f.read()

# Read lines
with open('file.txt', 'r') as f:
    lines = f.readlines()  # List with \n
    
# Read line by line (memory efficient)
with open('file.txt', 'r') as f:
    for line in f:
        print(line.strip())

# Write file
with open('output.txt', 'w') as f:
    f.write('content\n')

# Append
with open('output.txt', 'a') as f:
    f.write('more content\n')

# Binary files
with open('image.png', 'rb') as f:
    data = f.read()

# Check existence
import os
if os.path.exists('file.txt'):
    print('File exists')

# File info
import os.path
size = os.path.getsize('file.txt')
is_file = os.path.isfile('path')
is_dir = os.path.isdir('path')

Explanation: Always use with statement for file operations. It automatically closes files even if exceptions occur.

Path Operations

python

import os
from pathlib import Path

# os.path (traditional)
joined = os.path.join('data', 'file.txt')
dirname = os.path.dirname('/path/to/file.txt')
basename = os.path.basename('/path/to/file.txt')
name, ext = os.path.splitext('file.txt')

# pathlib (modern, recommended)
p = Path('data') / 'file.txt'
print(p.name)        # 'file.txt'
print(p.stem)        # 'file'
print(p.suffix)      # '.txt'
print(p.parent)      # 'data'
print(p.absolute())  # Absolute path

# Check existence
if p.exists():
    print('Exists')

# List directory
for item in Path('.').iterdir():
    print(item)

# Glob patterns
for txt_file in Path('.').glob('*.txt'):
    print(txt_file)

Explanation: pathlib is the modern way to work with paths. It's object-oriented and more intuitive than os.path.

Running Shell Commands

python

import subprocess

# Simple execution (blocking)
result = subprocess.run(['ls', '-la'], capture_output=True, text=True)
print(result.stdout)
print(result.returncode)

# Check for errors
result = subprocess.run(['false'], check=True)  # Raises CalledProcessError

# Shell mode (be careful with user input!)
result = subprocess.run('ls -la | grep txt', shell=True, capture_output=True, text=True)

# Get output directly
output = subprocess.check_output(['date'], text=True)

# Real-time output streaming
process = subprocess.Popen(
    ['ping', 'google.com'],
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    text=True
)

for line in process.stdout:
    print(line.strip())

Explanation: Use subprocess.run() for most cases. Avoid shell=True with untrusted input due to injection risks.

Error Handling

python

# Try-except
try:
    result = 10 / 0
except ZeroDivisionError as e:
    print(f"Error: {e}")
except Exception as e:
    print(f"Unexpected error: {e}")
finally:
    print("Cleanup")

# Multiple exceptions
try:
    risky_operation()
except (ValueError, TypeError) as e:
    print(f"Type error: {e}")

# Exit with code
import sys
sys.exit(0)  # Success
sys.exit(1)  # Error

# Context managers for cleanup
class Resource:
    def __enter__(self):
        print("Acquire")
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Release")
        return False  # Re-raise exception

with Resource() as r:
    pass  # Use resource

Common Data Operations

python

# JSON
import json

data = {'name': 'John', 'age': 30}
json_str = json.dumps(data, indent=2)
parsed = json.loads(json_str)

with open('data.json', 'w') as f:
    json.dump(data, f, indent=2)

with open('data.json', 'r') as f:
    loaded = json.load(f)

# CSV
import csv

with open('data.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['Name', 'Age'])
    writer.writerow(['John', 30])

with open('data.csv', 'r') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

# Dictionary reader (better for headers)
with open('data.csv', 'r') as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(row['Name'], row['Age'])

# YAML (pip install pyyaml)
import yaml

config = {'host': 'localhost', 'port': 8080}
with open('config.yaml', 'w') as f:
    yaml.dump(config, f)

with open('config.yaml', 'r') as f:
    config = yaml.safe_load(f)

List/Dict Comprehensions

python

# List comprehension
squares = [x**2 for x in range(10)]
evens = [x for x in range(10) if x % 2 == 0]

# Dict comprehension
word_lengths = {word: len(word) for word in ['cat', 'dog', 'elephant']}

# Set comprehension
unique_lengths = {len(word) for word in ['cat', 'dog', 'cat']}

# Generator expression (memory efficient)
large_sum = sum(x**2 for x in range(1000000))

Explanation: Comprehensions are more Pythonic and often faster than loops. Use generators for large datasets to save memory.

Functions and Decorators

python

# Basic function
def greet(name, greeting="Hello"):
    """Greet someone with a message."""
    return f"{greeting}, {name}!"

# Variable arguments
def sum_all(*args):
    return sum(args)

# Keyword arguments
def configure(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

# Decorator
import time

def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} took {end-start:.2f}s")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(1)
    return "Done"

User Input

python

# Simple input
name = input("Enter your name: ")
print(f"Hello {name}")

# Convert type
age = int(input("Enter age: "))

# With validation
while True:
    try:
        count = int(input("Enter a number: "))
        break
    except ValueError:
        print("Invalid number, try again")

Quick Reference Table

Feature Bash Node.js Python
Shebang #!/bin/bash #!/usr/bin/env node #!/usr/bin/env python3
Variables var="value" const var = "value" var = "value"
Access var $var or ${var} var var
Arguments $1 $2 $@ process.argv sys.argv or argparse
Exit code exit 1 process.exit(1) sys.exit(1)
Read file cat file or < file fs.readFileSync() open('file').read()
Write file echo "x" > file fs.writeFileSync() open('file', 'w').write()
Run command $(command) execSync() subprocess.run()
If statement if [ ]; then fi if () {} if:
For loop for i in ...; do done for (let i...) {} for i in ...:
Function func() { } function func() {} def func():
String concat "$a$b" `${a}${b}` f"{a}{b}"
Arrays arr=(1 2 3) arr = [1,2,3] arr = [1,2,3]

Best Practices

Bash

  • Always quote variables: "$var" prevents word splitting
  • Use [[ ]] instead of [ ] for conditionals
  • Enable strict mode: set -euo pipefail
  • Use functions for reusable code
  • Check command success: if command; then
  • Prefer $() over backticks for command substitution

Node.js

  • Use async/await for cleaner asynchronous code
  • Handle errors explicitly (try-catch, .catch())
  • Prefer const over let, avoid var
  • Use path.join() for cross-platform path handling
  • Validate and sanitize user input
  • Use libraries for argument parsing (minimist, yargs)
  • Listen for unhandled rejections and exceptions

Python

  • Follow PEP 8 style guide
  • Use with for file operations
  • Prefer pathlib over os.path for path operations
  • Use argparse for command-line arguments
  • Type hints for clarity: def func(name: str) -> int:
  • Use comprehensions when readable
  • Avoid shell=True in subprocess
  • Handle specific exceptions, not broad Exception

Common Mistakes

Bash

  • Forgetting to quote variables (word splitting issues)
  • Not checking if commands succeed before using their output
  • Using = in conditionals instead of -eq for numbers
  • Forgetting spaces in [ condition ]
  • Not making script executable (chmod +x)
  • Using backticks instead of $()

Node.js

  • Not handling promise rejections
  • Blocking the event loop with synchronous operations
  • Forgetting await with async functions
  • Not closing file handles or streams
  • Hardcoding paths instead of using path.join()
  • Using == instead of ===

Python

  • Not closing files (use with statement)
  • Using mutable default arguments: def func(list=[]):
  • Catching Exception too broadly
  • Not using virtual environments
  • Modifying list while iterating over it
  • Comparing with is instead of == for values
  • Forgetting to convert input() return value (always string)

TL;DR - Quick Commands

bash

# Bash
./script.sh arg1 arg2           # Run script
bash -x script.sh               # Debug mode
source script.sh                # Run in current shell

# Node.js
node script.js arg1 arg2        # Run script
node --inspect script.js        # Debug mode
npm init -y                     # Initialize package.json
npm install package             # Install dependency

# Python
python3 script.py arg1 arg2     # Run script
python3 -m pdb script.py        # Debug mode
python3 -m venv venv            # Create virtual environment
source venv/bin/activate        # Activate venv (Unix)
pip install package             # Install package
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment