Quick reference for writing scripts in Bash, Node.js, and Python with practical examples and explanations.
- 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
- 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
bash
#!/bin/bash
# This is a comment
# Make executable: chmod +x script.sh
# Run: ./script.shbash
# 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.
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: $#"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"
fiExplanation: Use [[ ]] over [ ] for better error handling and more features. Numeric comparisons use -eq, -ne, -gt, -lt, -ge, -le. String comparisons use =, !=, <, >.
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
donebash
# 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.
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 worldbash
# 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
donebash
# 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 stderrbash
# 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
fiExplanation: set -e stops script on any error. set -u catches typos in variable names. set -o pipefail makes pipes fail if any command fails.
javascript
#!/usr/bin/env node
// Make executable: chmod +x script.js
// Run: ./script.js or node script.js
console.log('Hello from Node.js');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.
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);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.
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');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.
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);
}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;
}
}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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Make executable: chmod +x script.py
# Run: ./script.py or python3 script.py
print("Hello from Python")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.
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()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.
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.
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.
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 resourcepython
# 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)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.
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"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")| 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] |
- 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
- Use
async/awaitfor cleaner asynchronous code - Handle errors explicitly (try-catch,
.catch()) - Prefer
constoverlet, avoidvar - 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
- Follow PEP 8 style guide
- Use
withfor file operations - Prefer
pathliboveros.pathfor path operations - Use
argparsefor command-line arguments - Type hints for clarity:
def func(name: str) -> int: - Use comprehensions when readable
- Avoid
shell=Truein subprocess - Handle specific exceptions, not broad
Exception
- Forgetting to quote variables (word splitting issues)
- Not checking if commands succeed before using their output
- Using
=in conditionals instead of-eqfor numbers - Forgetting spaces in
[ condition ] - Not making script executable (
chmod +x) - Using backticks instead of
$()
- Not handling promise rejections
- Blocking the event loop with synchronous operations
- Forgetting
awaitwith async functions - Not closing file handles or streams
- Hardcoding paths instead of using
path.join() - Using
==instead of===
- Not closing files (use
withstatement) - Using mutable default arguments:
def func(list=[]): - Catching
Exceptiontoo broadly - Not using virtual environments
- Modifying list while iterating over it
- Comparing with
isinstead of==for values - Forgetting to convert
input()return value (always string)
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