How to Convert Excel to CSV Without Opening Excel
Convert .xlsx files to CSV on any OS — Python, PowerShell, LibreOffice CLI, and a browser tool. Covers batch conversion, multi-sheet files, and encoding issues.
- Why not just "Save As CSV" in Excel?
- Method 1: Python with pandas (cross-platform, recommended)
- Install
- Single file, single sheet
- Single file, specific sheet by name
- Export all sheets from one workbook
- Batch convert an entire folder
- Encoding
- Date formatting
- Where pandas falls short
- Method 2: Python with openpyxl (more control)
- Install
- Basic conversion
- Handling merged cells
- Method 3: PowerShell (Windows, no installs)
- Batch convert with PowerShell
- Caveats
- Method 4: LibreOffice CLI (Linux and macOS)
- Install
- Convert a single file
- Specify output directory
- Batch convert
- Encoding and delimiter
- Caveats
- Method 5: Browser tool (no setup, one-off files)
- Comparison
- Common problems
- Output CSV has garbled characters
- Date columns show as numbers (e.g. `45305`)
- Empty rows in the CSV
- Only the first sheet was exported
- Related tools
How to Convert Excel to CSV Without Opening Excel
Opening Excel just to save a file as CSV is a tax on your time — especially when you have dozens of files, you're on a server without a GUI, or you simply don't have Excel installed. There are faster paths.
This guide covers four methods: Python (the most portable), PowerShell (Windows-native, no installs), LibreOffice CLI (Linux/macOS), and a browser-based converter for one-off files. Each section includes the exact commands, the edge cases to watch for, and when to pick that method over the others.
Why not just "Save As CSV" in Excel?
If you have Excel open anyway, File → Save As → CSV works fine. But it has limitations:
- One sheet at a time — Excel only exports the active sheet. Multi-sheet workbooks require you to repeat the process for each sheet.
- Format loss warnings — Excel shows a dialog every time about features not supported in CSV.
- Can't automate — you can't script "Save As CSV" without Excel macros or COM automation.
- Not available on Linux or macOS without Office — Excel is a Windows/macOS application and requires a license.
The methods below sidestep all of that.
Method 1: Python with pandas (cross-platform, recommended)
pandas is the fastest path to a working script, and it runs on Windows, macOS, and Linux.
Install
pip install pandas openpyxl
openpyxl is the Excel reading engine; pandas requires it for .xlsx files.
Single file, single sheet
import pandas as pd
df = pd.read_excel("report.xlsx", sheet_name=0)
df.to_csv("report.csv", index=False)
sheet_name=0 reads the first sheet. index=False omits the pandas row index from the CSV output — you almost always want this.
Single file, specific sheet by name
df = pd.read_excel("report.xlsx", sheet_name="Sales")
df.to_csv("sales.csv", index=False)
Export all sheets from one workbook
import pandas as pd
xl = pd.ExcelFile("report.xlsx")
for sheet in xl.sheet_names:
df = xl.parse(sheet)
safe_name = sheet.replace(" ", "_").replace("/", "-")
df.to_csv(f"{safe_name}.csv", index=False)
Each sheet becomes its own CSV file, named after the sheet.
Batch convert an entire folder
import pandas as pd
from pathlib import Path
input_dir = Path("excel_files")
output_dir = Path("csv_output")
output_dir.mkdir(exist_ok=True)
for xlsx_path in input_dir.glob("*.xlsx"):
df = pd.read_excel(xlsx_path, sheet_name=0)
out_path = output_dir / xlsx_path.with_suffix(".csv").name
df.to_csv(out_path, index=False)
print(f"Converted: {xlsx_path.name} → {out_path.name}")
Run this from any directory and it converts every .xlsx in excel_files/ to a matching .csv in csv_output/.
Encoding
to_csv defaults to UTF-8 encoding, which is what most downstream tools expect. If the recipient opens the CSV in Excel on Windows and sees garbled characters, add encoding="utf-8-sig" — this writes a UTF-8 BOM that Excel on Windows needs to detect encoding correctly:
df.to_csv("report.csv", index=False, encoding="utf-8-sig")
Date formatting
By default, pandas serializes dates as "2024-01-14 00:00:00" — the full datetime string even for date-only columns. To get cleaner ISO dates:
# Option 1: format at write time
df.to_csv("report.csv", index=False, date_format="%Y-%m-%d")
# Option 2: convert the column first
df["joined"] = df["joined"].dt.strftime("%Y-%m-%d")
df.to_csv("report.csv", index=False)
Where pandas falls short
- Merged cells — only the top-left cell has a value; others are
NaN. You'll get blank cells in the CSV where merged cells span multiple rows or columns. - Formulas — pandas reads the last-computed value, not the formula. Usually correct, but stale caches cause silent errors.
.xls(old format) — requiresxlrdinstead ofopenpyxl. Installpip install xlrdand passengine="xlrd".
Method 2: Python with openpyxl (more control)
Use openpyxl directly when you need to handle merged cells, read cell formatting, or access metadata that pandas abstracts away.
Install
pip install openpyxl
Basic conversion
from openpyxl import load_workbook
import csv
wb = load_workbook("report.xlsx", data_only=True)
ws = wb.active
with open("report.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
for row in ws.iter_rows(values_only=True):
writer.writerow(row)
data_only=True returns the last computed value for formula cells instead of the formula string.
Handling merged cells
In openpyxl, merged cells beyond the top-left return None. If a merged cell spans rows (like a category label), you'll get blank cells in the CSV unless you forward-fill:
from openpyxl import load_workbook
from openpyxl.utils import range_boundaries
import csv
wb = load_workbook("report.xlsx", data_only=True)
ws = wb.active
# Unmerge: copy the top-left value into all cells of the merged range
for merged_range in list(ws.merged_cells.ranges):
top_left = ws.cell(merged_range.min_row, merged_range.min_col).value
ws.unmerge_cells(str(merged_range))
for row in ws.iter_rows(
min_row=merged_range.min_row, max_row=merged_range.max_row,
min_col=merged_range.min_col, max_col=merged_range.max_col
):
for cell in row:
cell.value = top_left
with open("report.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
for row in ws.iter_rows(values_only=True):
writer.writerow(row)
Method 3: PowerShell (Windows, no installs)
On Windows, PowerShell can open an Excel COM object without displaying the Excel window, convert the file, and close — no Excel license dialog, no GUI. This requires Excel to be installed, but doesn't require you to open it manually.
param(
[string]$InputPath = "report.xlsx",
[string]$OutputPath = "report.csv"
)
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$excel.DisplayAlerts = $false
$workbook = $excel.Workbooks.Open((Resolve-Path $InputPath).Path)
$worksheet = $workbook.Sheets.Item(1)
$workbook.SaveAs(
(Join-Path (Get-Location) $OutputPath),
6 # xlCSV format code
)
$workbook.Close($false)
$excel.Quit()
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel) | Out-Null
Save as excel-to-csv.ps1 and run:
.\excel-to-csv.ps1 -InputPath "report.xlsx" -OutputPath "report.csv"
Batch convert with PowerShell
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$excel.DisplayAlerts = $false
Get-ChildItem ".\excel_files\*.xlsx" | ForEach-Object {
$wb = $excel.Workbooks.Open($_.FullName)
$outPath = Join-Path ".\csv_output" ($_.BaseName + ".csv")
$wb.SaveAs($outPath, 6)
$wb.Close($false)
Write-Host "Converted: $($_.Name)"
}
$excel.Quit()
Caveats
- Requires Excel installed — the COM object is part of the Excel installation. Without Excel, this script won't run.
- Format code 6 is
xlCSV. For UTF-8 CSV use format code62(xlCSVUTF8) — available in Excel 2016+. - Only exports the first sheet — to export all sheets, loop over
$workbook.Sheets. - Not available on Linux or macOS — COM automation is Windows-only.
Method 4: LibreOffice CLI (Linux and macOS)
LibreOffice ships with a headless mode that converts Office files without opening a window. It's available on Linux (via package manager) and macOS (via the installer or Homebrew).
Install
# Ubuntu / Debian
sudo apt install libreoffice
# macOS via Homebrew
brew install --cask libreoffice
Convert a single file
libreoffice --headless --convert-to csv report.xlsx
This produces report.csv in the current directory.
Specify output directory
libreoffice --headless --convert-to csv --outdir ./csv_output report.xlsx
Batch convert
libreoffice --headless --convert-to csv --outdir ./csv_output excel_files/*.xlsx
LibreOffice processes all matching files in one pass — faster than looping in shell.
Encoding and delimiter
The default output is UTF-8 with comma delimiter. To customize (e.g. semicolons for European locale):
libreoffice --headless --convert-to "csv:Text - txt - csv (StarCalc):44,34,76,1,,0,false,true,false,false,false,-1" report.xlsx
The filter string parameters map to: field_delimiter(44=comma),text_delimiter(34=quote),charset(76=UTF-8),first_row,…. Change 44 to 59 for semicolon.
Caveats
- Only exports the first sheet by default. To export all sheets, use a LibreOffice macro or convert each sheet with a loop.
- Date formatting matches LibreOffice's locale settings, which may differ from Excel's. Verify date columns in the output.
- Large files (100k+ rows) are slower than pandas — LibreOffice loads the full spreadsheet rendering engine.
Method 5: Browser tool (no setup, one-off files)
For a single file when you don't want to write code or install anything, csvjson.tools Excel to CSV converts in the browser:
- Upload your
.xlsxfile - Select the sheet if the workbook has multiple sheets
- Download the CSV
The conversion runs client-side — your file is never uploaded to a server. Numbers, booleans, and dates are handled correctly: ISO date strings, not Excel serial numbers.
Also useful as a sanity check before automating — paste the output into CSV Viewer to spot encoding or delimiter issues before writing a script.
Comparison
| Method | OS | Excel required | Batch support | Merged cells | Speed | |---|---|---|---|---|---| | pandas | Any | No | Yes | NaN (needs fill) | Fast | | openpyxl | Any | No | Yes | Manual fill | Moderate | | PowerShell COM | Windows only | Yes | Yes | Yes (Excel handles) | Moderate | | LibreOffice CLI | Linux/macOS | No | Yes | Partial | Slow on large files | | Browser tool | Any | No | No | Yes | Instant |
Common problems
Output CSV has garbled characters
The file likely contains non-ASCII characters (accented letters, CJK, symbols). The CSV is probably being opened in Excel on Windows, which defaults to the system code page (Windows-1252) rather than UTF-8.
Fix: write UTF-8 with BOM (encoding="utf-8-sig" in pandas, or format code 62 in the PowerShell COM script). Excel on Windows recognizes the BOM and reads UTF-8 correctly.
Date columns show as numbers (e.g. 45305)
The converter is emitting the raw Excel date serial number. In pandas, date columns should be inferred automatically if the cells are formatted as Date in Excel. If not, use parse_dates=["column_name"] or convert after reading: df["date_col"] = pd.to_datetime(df["date_col"]).dt.strftime("%Y-%m-%d").
Empty rows in the CSV
Blank rows in the spreadsheet pass through to the CSV. In pandas, remove them with df.dropna(how="all") before calling to_csv. In openpyxl, check all(v is None for v in row) before writing each row.
Only the first sheet was exported
pandas sheet_name=0 reads the first sheet only. Use sheet_name=None to get a dict of all sheets, or loop over xl.sheet_names. LibreOffice CLI also exports only the first sheet by default — use a macro or script to loop over sheets.
Related tools
- Excel to CSV — upload an xlsx and download a clean CSV in one click
- Excel to JSON — convert to JSON instead of CSV, with full type preservation
- CSV Cleaner — fix encoding issues, inconsistent columns, and stray quotes after conversion
- CSV to JSON — take your new CSV further and convert to JSON for APIs or code