Filter CSV rows with Python without changing your original file
Keep rows that match one exact value, save a separate CSV, and verify the selected count before using the result.
In this guide
If you only need the pending orders from a larger export, you do not have to delete the other rows by hand. A small Python script can keep the matching records in a separate file and leave the original alone.
This guide uses one exact text condition: keep rows where a named column equals a value you provide. It does not guess at similar names, compare dates, or decide which orders deserve follow-up.
Prepare the file and the condition
You need Python 3.12 or newer, a text editor, and a terminal. Check py --version in Windows PowerShell or python3 --version in macOS/Linux Terminal. If it is missing, install Python from the official downloads page, reopen the terminal, and retry. No packages or server are required.
Create a folder named csv-filter. Put a copy of your small UTF-8, comma-separated CSV with a header in it as orders.csv. The file needs to fit comfortably in memory. Alternatively, save this fictional example as plain text:
order_id,item,status
1001,"Notebook, blue",pending
1002,Mug,paid
1003,Pencil,pending
Our condition is status equals pending. The result should contain orders 1001 and 1003: two records, plus the header.
For your own file, inspect the exact column name and an actual cell before choosing the condition. pending, Pending, and pending are different values. That last one has a trailing space.
Save the script
Save this as filter_csv.py in the same folder, using the .py extension rather than .py.txt:
import argparse
import csv
from pathlib import Path
parser = argparse.ArgumentParser(description="Keep CSV rows matching one value.")
parser.add_argument("file")
parser.add_argument("--column", required=True)
parser.add_argument("--equals", required=True)
parser.add_argument("--output", required=True)
args = parser.parse_args()
try:
if Path(args.file).resolve() == Path(args.output).resolve():
raise ValueError("Input and output must be different files.")
selected = []
total = 0
with open(args.file, encoding="utf-8-sig", newline="") as source:
reader = csv.reader(source, strict=True)
header = next(reader, None)
if not header or any(not name.strip() for name in header):
raise ValueError("Missing column names.")
if len(header) != len(set(header)):
raise ValueError("Column names must be unique.")
if args.column not in header:
raise ValueError(f"Column not found: {args.column}")
index = header.index(args.column)
for row in reader:
if not row:
continue
if len(row) != len(header):
raise ValueError(f"Wrong cell count near line {reader.line_num}.")
total += 1
if row[index] == args.equals:
selected.append(row)
# Validate the entire input before creating a new output file.
with open(args.output, "x", encoding="utf-8", newline="") as target:
writer = csv.writer(target)
writer.writerow(header)
writer.writerows(selected)
print(f"Kept {len(selected)} of {total} records in {args.output}.")
except (OSError, ValueError, csv.Error) as error:
parser.exit(1, f"Stopped: {error}\n")
It preserves the selected rows in their original order, including all their columns. It does not remove duplicates or alter the values.
Run the filter
Open a terminal in csv-filter. On Windows, type powershell in File Explorer’s address bar. On macOS/Linux, type cd , drag the folder into Terminal, and press Enter.
Windows PowerShell:
py filter_csv.py orders.csv --column status --equals pending --output pending-orders.csv
macOS/Linux:
python3 filter_csv.py orders.csv --column status --equals pending --output pending-orders.csv
For a column or value containing spaces, use quotes: --column "Order status" --equals "Awaiting payment". Replace the input name, column, value, and output name in this same command; you do not need to edit the script.
Verify what was kept
The example should report Kept 2 of 3 records in pending-orders.csv. Open the output in a text editor and check:
order_id,item,status
1001,"Notebook, blue",pending
1003,Pencil,pending
For your own file, inspect a matching row in both input and output, then confirm that a known nonmatching row is absent. The count excludes the header. A file with zero matches is still created with a header, so check the message rather than assuming a newly created file contains useful records.
When importing identifiers into a spreadsheet, set those columns to text if leading zeroes matter. Treat untrusted formula-like values as text too. This script filters records; it does not sanitize spreadsheet formulas.
Fix a surprising result
- Zero matches: compare the spelling, capitalization, and spaces with an actual cell. Do not broaden the condition until you know why it failed.
- Column not found: inspect the header. A semicolon-separated export may appear as one column; re-export it with commas instead of globally replacing punctuation.
- Wrong cell count or quotation error: inspect the reported area in a text editor. A quoted value can span several lines, so the line number is a location hint.
- Encoding error: obtain a UTF-8 copy from the exporting application.
- File already exists: choose a fresh output name. Neither the input nor an earlier result is overwritten.
The script finishes immediately after the report; no automation remains active. Keep the full export until the filtered result has served its purpose. If you next need to combine several exports, use the CSV merge guide.
Sources and useful links
- Python CSV reference — explains quoting, delimiters, and handling newlines correctly.
- Python argument parsing — reference for choosing the input and condition at run time.
- Python file modes — explains how
xprevents replacing an existing output.