Bash
Python
Programming
Scripting
Code Conversion

How to implement common bash idioms in Python?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Many tasks commonly done in Bash — listing files, piping commands, reading environment variables, text processing — have cleaner and more portable equivalents in Python. Python's os, pathlib, subprocess, and shutil modules cover most of what Bash scripts do, with better error handling and cross-platform support. This article maps the most common Bash idioms to their Python equivalents.

Listing and Iterating Over Files

bash
1# Bash: loop over files matching a pattern
2for f in *.txt; do
3    echo "$f"
4done
python
1# Python: pathlib (recommended)
2from pathlib import Path
3
4for f in Path(".").glob("*.txt"):
5    print(f)
6
7# Python: os module
8import os
9for f in os.listdir("."):
10    if f.endswith(".txt"):
11        print(f)
12
13# Recursive glob
14for f in Path(".").rglob("*.txt"):
15    print(f)

Running Shell Commands

bash
# Bash: run a command and capture output
output=$(curl -s https://example.com)
python
1import subprocess
2
3# Run and capture output
4result = subprocess.run(
5    ["curl", "-s", "https://example.com"],
6    capture_output=True, text=True, check=True
7)
8output = result.stdout
9
10# Pipe between commands (equivalent to: ps aux | grep python)
11ps = subprocess.Popen(["ps", "aux"], stdout=subprocess.PIPE)
12grep = subprocess.Popen(["grep", "python"], stdin=ps.stdout, stdout=subprocess.PIPE, text=True)
13ps.stdout.close()
14output = grep.communicate()[0]

Environment Variables

bash
1# Bash
2export APP_ENV=production
3echo "$APP_ENV"
4echo "${APP_ENV:-default_value}"
python
1import os
2
3# Get with default
4env = os.environ.get("APP_ENV", "default_value")
5
6# Set
7os.environ["APP_ENV"] = "production"
8
9# Check existence
10if "APP_ENV" in os.environ:
11    print(os.environ["APP_ENV"])

File Tests (Existence, Type, Size)

bash
1# Bash
2[ -f "file.txt" ] && echo "File exists"
3[ -d "mydir" ] && echo "Directory exists"
4[ -s "file.txt" ] && echo "File is non-empty"
python
1from pathlib import Path
2
3p = Path("file.txt")
4
5if p.is_file():
6    print("File exists")
7
8if Path("mydir").is_dir():
9    print("Directory exists")
10
11if p.exists() and p.stat().st_size > 0:
12    print("File is non-empty")

Reading and Writing Files

bash
1# Bash: read file line by line
2while IFS= read -r line; do
3    echo "$line"
4done < file.txt
5
6# Bash: write to file
7echo "hello" > output.txt
8echo "world" >> output.txt
python
1# Python: read line by line
2with open("file.txt") as f:
3    for line in f:
4        print(line.rstrip())
5
6# Python: write (overwrite)
7with open("output.txt", "w") as f:
8    f.write("hello\n")
9
10# Python: append
11with open("output.txt", "a") as f:
12    f.write("world\n")

Text Processing (grep, sed, awk)

bash
1# Bash: grep
2grep "error" logfile.txt
3grep -c "error" logfile.txt   # count matches
4
5# Bash: sed substitution
6sed 's/old/new/g' file.txt
python
1import re
2
3# grep equivalent
4with open("logfile.txt") as f:
5    matches = [line for line in f if "error" in line]
6    print(f"Found {len(matches)} matches")
7
8# sed equivalent (regex substitution)
9with open("file.txt") as f:
10    content = f.read()
11modified = re.sub(r"old", "new", content)
12
13# awk: print specific columns
14with open("data.txt") as f:
15    for line in f:
16        cols = line.split()
17        if len(cols) >= 3:
18            print(cols[0], cols[2])  # print columns 1 and 3

Copying, Moving, and Deleting Files

bash
1cp source.txt dest.txt
2mv old.txt new.txt
3rm file.txt
4rm -rf mydir/
python
1import shutil
2from pathlib import Path
3
4shutil.copy2("source.txt", "dest.txt")   # cp (preserves metadata)
5shutil.move("old.txt", "new.txt")         # mv
6Path("file.txt").unlink(missing_ok=True)  # rm
7shutil.rmtree("mydir")                    # rm -rf

Command-Line Arguments

bash
# Bash: $1, $2, $@
echo "First arg: $1"
echo "All args: $@"
python
1import sys
2
3# sys.argv
4print(f"First arg: {sys.argv[1]}")
5print(f"All args: {sys.argv[1:]}")
6
7# argparse (for real scripts)
8import argparse
9parser = argparse.ArgumentParser()
10parser.add_argument("--name", required=True)
11parser.add_argument("--verbose", action="store_true")
12args = parser.parse_args()
13print(args.name)

Exit Codes and Error Handling

bash
# Bash
command || { echo "Failed"; exit 1; }
set -e  # Exit on any error
python
1import subprocess
2import sys
3
4try:
5    subprocess.run(["command"], check=True)
6except subprocess.CalledProcessError as e:
7    print(f"Failed with exit code {e.returncode}")
8    sys.exit(1)

Common Pitfalls

  • Using os.system() instead of subprocess.run(): os.system() does not capture output, does not raise on failure, and is vulnerable to shell injection. Always use subprocess.run() with a list of arguments.
  • Using shell=True unnecessarily: subprocess.run("ls -l", shell=True) invokes the shell and is a security risk if the command includes user input. Use subprocess.run(["ls", "-l"]) (list form) instead.
  • Forgetting text=True in subprocess: Without text=True, subprocess.run returns bytes (b"output") instead of strings. Add text=True (or encoding="utf-8") for string output.
  • Using string concatenation for file paths: base + "/" + name breaks on Windows. Use Path(base) / name or os.path.join(base, name) for portable paths.
  • Not handling file encoding: Bash reads files as bytes by default. Python 3 opens files as UTF-8 (on most systems). If the file uses a different encoding, pass encoding="latin-1" or the correct encoding to open().

Summary

  • Use pathlib.Path for file operations — it replaces ls, test -f, mkdir, and path manipulation
  • Use subprocess.run() with list arguments instead of os.system() for running commands
  • Use os.environ.get() with a default value instead of ${VAR:-default}
  • Use re.sub() for sed-like substitutions and list comprehensions for grep-like filtering
  • Use shutil for cp, mv, and rm -rf equivalents
  • Use argparse for command-line argument parsing instead of sys.argv for real scripts

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.