Overview
The dos2unix command is an essential utility for converting text files between DOS/Windows and Unix/Linux line ending formats. This seemingly simple task is critical because different operating systems use different control characters to mark the end of a line, and these differences can cause scripts to fail, programs to malfunction, and data to be misinterpreted.
The Problem:
- DOS/Windows: Uses CR+LF (Carriage Return + Line Feed:
\r\nor0x0D 0x0A) - Unix/Linux: Uses LF only (Line Feed:
\nor0x0A) - Old Mac: Used CR only (Carriage Return:
\ror0x0D) - rarely seen now
Why This Matters:
- Shell scripts with DOS line endings won't execute (#!/bin/bash^M errors)
- Configuration files may be parsed incorrectly
- Programs reading line-by-line may include CR in the data
- Git commits show every line as changed even when content is identical
- Text processing tools may behave unexpectedly
The difference comes from historical typewriter mechanics. A carriage return moved the print head back to the start, and a line feed advanced the paper. Teletype machines needed both operations. Unix simplified this to just line feed, while DOS/Windows kept both for compatibility. In hex dumps, you'll see DOS line endings as 0D 0A and Unix as just 0A. In vim or cat -A, DOS line endings appear as ^M at the end of each line.
Common Options
| Option | Description |
|---|---|
| -k | Keep file date stamp (don't update modification time) |
| -n | New file mode (write to new file, keep original) |
| -o | Write to original file (overwrite - default behavior) |
| -q | Quiet mode (suppress warnings) |
| -v | Verbose mode (show files being converted) |
| -b | Make backup of original file (.bak extension) |
| -f | Force conversion of binary files (use with caution!) |
| -c | Specify conversion mode (ascii, 7bit, iso, mac) |
| -i | Display file information (line endings, byte order mark, encoding) |
Detailed Examples
Basic File Conversion - Detecting the Problem
First, let's identify a file with DOS line endings and convert it.
# Check if file has DOS line endings using cat -A
cat -A script.sh
^M characters at the end of each line indicate carriage returns (CR). The $ shows the line feed (LF). This is the DOS/Windows format: CR+LF.
# Try to run the script - it will fail!
./script.sh
/bin/bash^M (with a carriage return in the name), which doesn't exist. The CR character is being treated as part of the interpreter path.
# Convert the file to Unix format
dos2unix script.sh
# Verify the conversion
cat -A script.sh
^M characters are gone. Now we only see $ at the end of lines (the LF character). The script will now execute properly.
# Now it works!
./script.sh
Converting Multiple Files at Once
Process multiple files in a single command, essential for batch operations.
# Convert multiple specific files
dos2unix file1.txt file2.txt file3.sh config.conf
# Convert all .sh files in current directory
dos2unix *.sh
# Convert all .txt files recursively
find . -name "*.txt" -exec dos2unix {} \;
# Convert all files in a directory
dos2unix /path/to/scripts/*
find for recursive operations. This is invaluable when you've received a directory of files from Windows systems and need to convert them all.
-i option to check which files actually need conversion: dos2unix -i *.txt. This shows you which files have DOS line endings without modifying them.
Creating Backups with -b Option
Always a good practice to keep the original file when you're not certain about the conversion.
# Convert file and create backup with .bak extension
dos2unix -b important_config.conf
# List files to see backup was created
ls -lh important_config*
-b option tells dos2unix to create a backup of the original file before converting it. The backup has a .bak extension. Notice the original file (now converted) is slightly smaller - those extra 4 bytes were the carriage return characters that were removed from 4 lines of text.
# Compare the files to see the difference
diff important_config.conf.bak important_config.conf
# Or use cmp to see the byte-level differences
cmp -l important_config.conf.bak important_config.conf | head -5
-b: by default it uses .bak, but some versions allow custom suffixes in configuration.
New File Mode with -n Option
Write the converted output to a new file while preserving the original unchanged.
# Convert and write to new file
dos2unix -n windows_file.txt unix_file.txt
# Verify both files exist
ls -lh *_file.txt
# Check line endings in both files
file windows_file.txt unix_file.txt
-n option (new file mode) takes two arguments: the input file and the output file. The original file remains completely unchanged, and a new converted file is created. The file command confirms the line ending formats: "CRLF line terminators" for DOS format, and plain "ASCII text" for Unix format.
# Convert multiple files to new files with different extension
for file in *.txt.dos; do
dos2unix -n "$file" "${file%.dos}"
done
# This converts file1.txt.dos → file1.txt, file2.txt.dos → file2.txt, etc.
Information Mode with -i Option
Check file information without converting - see what format files are in before making changes.
# Check file information
dos2unix -i script1.sh script2.sh config.txt data.csv
- Column 1: Number of Unix line endings (LF only)
- Column 2: Number of DOS line endings (CR+LF)
- Column 3: Number of Mac line endings (CR only)
- Column 4: BOM status (Byte Order Mark)
- Column 5: File type (text or binary)
In this example, script1.sh and script2.sh have only Unix line endings (already correct), config.txt has 8 DOS line endings (needs conversion), and data.csv has 150 DOS line endings (definitely needs conversion).
# Check all files in directory and show only those needing conversion
dos2unix -i *.txt | awk '$2 > 0 {print $6}'
Keeping File Timestamps with -k Option
Preserve the original modification time when converting files - important for build systems and version control.
# Check original timestamp
ls -l --time-style=full-iso oldfile.txt
# Convert WITHOUT -k (default behavior)
dos2unix oldfile.txt
ls -l --time-style=full-iso oldfile.txt
# Now convert WITH -k to preserve timestamp
dos2unix -k anotherfile.txt
# Check the timestamp
ls -l --time-style=full-iso anotherfile.txt
-k option, the file's modification time remains unchanged. The file is converted, but it appears as if it hasn't been touched.
Converting Files in a Script Pipeline
Integrate dos2unix into automated workflows and deployment scripts.
#!/bin/bash
# Deployment script that ensures all scripts have Unix line endings
DEPLOY_DIR="/opt/application/scripts"
LOG_FILE="/var/log/deployment.log"
echo "========================================" | tee -a "$LOG_FILE"
echo "Deployment started: $(date)" | tee -a "$LOG_FILE"
echo "========================================" | tee -a "$LOG_FILE"
# Find all shell scripts and convert them
echo "Converting shell scripts to Unix format..." | tee -a "$LOG_FILE"
find "$DEPLOY_DIR" -type f -name "*.sh" | while read script; do
# Check if conversion is needed
DOSLINES=$(dos2unix -i "$script" | awk '{print $2}')
if [ "$DOSLINES" -gt 0 ]; then
echo " Converting: $script (found $DOSLINES DOS line endings)" | tee -a "$LOG_FILE"
dos2unix -k -b "$script" 2>&1 | tee -a "$LOG_FILE"
# Make executable if it has a shebang
if head -1 "$script" | grep -q "^#!"; then
chmod +x "$script"
echo " Made executable: $script" | tee -a "$LOG_FILE"
fi
else
echo " Skipping: $script (already Unix format)" | tee -a "$LOG_FILE"
fi
done
echo "========================================" | tee -a "$LOG_FILE"
echo "Deployment completed: $(date)" | tee -a "$LOG_FILE"
echo "========================================" | tee -a "$LOG_FILE"
-k, creates backups with -b, and automatically makes scripts executable if they have a shebang line. All actions are logged for audit purposes.
Handling Mixed Line Endings
Some files have both DOS and Unix line endings mixed together - a particularly troublesome situation.
# Check a file with mixed line endings
dos2unix -i mixed_file.txt
# Look at the problematic sections with cat -A
cat -A mixed_file.txt | head -20
$ (Unix) and others with ^M$ (DOS). This inconsistency can cause parsing problems in some applications.
# Convert to consistent Unix format
dos2unix mixed_file.txt
# Verify all line endings are now consistent
dos2unix -i mixed_file.txt
git config --global core.autocrlf input on Linux/Mac.
Verbose Mode and Quiet Mode
Control the output verbosity based on your needs - detailed feedback or silent operation.
# Verbose mode - detailed output
dos2unix -v file1.txt file2.txt file3.txt
-v option provides detailed information about each conversion, including how many line breaks were converted and any warnings (like binary symbols detected).
# Quiet mode - suppress warnings
dos2unix -q file1.txt file2.txt file3.txt
# Silent operation - redirect all output
dos2unix file1.txt 2>/dev/null
-q option suppresses warning messages. This is useful in automated scripts where you don't want warnings cluttering your logs. The second example shows completely silent operation by redirecting stderr to /dev/null.
- Verbose (-v): When troubleshooting or during initial testing
- Normal (default): For regular interactive use
- Quiet (-q): In cron jobs or automated scripts
- Silent (redirect stderr): When you absolutely want no output
# Check if conversion succeeded
dos2unix -q important.txt
if [ $? -eq 0 ]; then
echo "Conversion successful"
else
echo "Conversion failed!" >&2
exit 1
fi
Complete Workflow: From Detection to Conversion
A comprehensive example showing best practices for safe file conversion in a production environment.
#!/bin/bash
# Safe file conversion workflow with full error checking
TARGET_DIR="/opt/configs"
BACKUP_DIR="/opt/configs/backup_$(date +%Y%m%d_%H%M%S)"
LOG_FILE="/var/log/dos2unix_conversion.log"
# Function to log messages
log_message() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# Check if dos2unix is installed
if ! command -v dos2unix &> /dev/null; then
log_message "ERROR: dos2unix is not installed!"
log_message "Install with: yum install dos2unix"
exit 1
fi
log_message "Starting line ending conversion process"
log_message "Target directory: $TARGET_DIR"
# Create backup directory
mkdir -p "$BACKUP_DIR"
log_message "Created backup directory: $BACKUP_DIR"
# Find all text files and check their format
log_message "Scanning for files with DOS line endings..."
FILES_TO_CONVERT=()
while IFS= read -r file; do
# Get DOS line count
INFO=$(dos2unix -i "$file")
DOS_COUNT=$(echo "$INFO" | awk '{print $2}')
if [ "$DOS_COUNT" -gt 0 ]; then
FILES_TO_CONVERT+=("$file")
log_message " Found: $file ($DOS_COUNT DOS line endings)"
fi
done < <(find "$TARGET_DIR" -type f \( -name "*.conf" -o -name "*.cfg" -o -name "*.sh" \))
# Report findings
if [ ${#FILES_TO_CONVERT[@]} -eq 0 ]; then
log_message "No files need conversion. All files already have Unix line endings."
exit 0
fi
log_message "Found ${#FILES_TO_CONVERT[@]} file(s) needing conversion"
# Ask for confirmation in interactive mode
if [ -t 0 ]; then
echo ""
read -p "Proceed with conversion? (yes/no): " CONFIRM
if [ "$CONFIRM" != "yes" ]; then
log_message "Conversion cancelled by user"
exit 0
fi
fi
# Process each file
log_message "Beginning conversion..."
CONVERTED=0
FAILED=0
for file in "${FILES_TO_CONVERT[@]}"; do
# Create backup
cp -p "$file" "$BACKUP_DIR/"
# Convert with timestamp preservation
if dos2unix -k "$file" 2>&1 | tee -a "$LOG_FILE"; then
log_message " ✓ Converted: $file"
((CONVERTED++))
# If it's a shell script, ensure it's executable
if [[ "$file" == *.sh ]] && head -1 "$file" | grep -q "^#!"; then
chmod +x "$file"
log_message " Made executable: $file"
fi
else
log_message " ✗ FAILED: $file"
((FAILED++))
# Restore from backup
cp -p "$BACKUP_DIR/$(basename "$file")" "$file"
log_message " Restored from backup"
fi
done
# Final report
log_message "========================================="
log_message "Conversion Summary:"
log_message " Successfully converted: $CONVERTED"
log_message " Failed: $FAILED"
log_message " Backups stored in: $BACKUP_DIR"
log_message "========================================="
# Clean up old backups (keep last 5)
log_message "Cleaning up old backups..."
OLD_BACKUPS=$(ls -dt /opt/configs/backup_* 2>/dev/null | tail -n +6)
if [ -n "$OLD_BACKUPS" ]; then
echo "$OLD_BACKUPS" | xargs rm -rf
log_message "Removed old backups"
fi
log_message "Process completed successfully"
- Checks for dos2unix installation before proceeding
- Scans and identifies files needing conversion
- Creates timestamped backups of all files
- Preserves file timestamps during conversion
- Logs all operations with timestamps
- Handles errors gracefully with automatic restoration
- Makes shell scripts executable after conversion
- Provides summary statistics
- Manages backup retention (keeps last 5)
- Interactive confirmation in terminal mode
Comparison: Line Ending Conversion Methods
| Method | Pros | Cons | Best For |
|---|---|---|---|
| dos2unix | Purpose-built, safe, preserves timestamps, handles edge cases | Requires installation (not always available) | Production use, batch operations |
| sed 's/\r$//' | Available everywhere, no installation needed | Doesn't preserve timestamps, requires redirection | Quick one-off fixes, minimal systems |
| tr -d '\r' | Simple, available everywhere | Removes ALL carriage returns (dangerous!), no backups | Simple cases only, not recommended |
| vim (set ff=unix) | Interactive, visual feedback | Manual, one file at a time, not scriptable | Single file editing during development |
| Git (autocrlf) | Automatic, repository-wide | Can cause mixed line endings, needs careful setup | Source code in Git repositories |
Additional Information
Dos2unix will refuse to convert binary files by default because changing bytes in binary files will corrupt them. If you see "Binary symbol found" warnings, investigate the file. It might be:
- Actually a binary file (don't convert!)
- A text file with unusual characters (check with
filecommand) - An encoded file (UTF-16, UTF-32, etc.) that needs different handling
Use -f to force conversion ONLY if you're absolutely certain it's safe.
# Oracle Linux / RHEL / CentOS
sudo yum install dos2unix
# Debian / Ubuntu
sudo apt-get install dos2unix
# Verify installation
dos2unix --version
- unix2dos: Converts Unix line endings to DOS (opposite direction)
- mac2unix: Converts old Mac line endings to Unix
- unix2mac: Converts Unix line endings to old Mac format
- file: Identifies file type and line ending format
- cat -A: Visualizes invisible characters including line endings
# Using sed (preserves file in place)
sed -i 's/\r$//' filename.txt
# Using sed (create new file)
sed 's/\r$//' inputfile.txt > outputfile.txt
# Using tr (dangerous - removes ALL \r)
tr -d '\r' < inputfile.txt > outputfile.txt
# Using perl
perl -pi -e 's/\r\n/\n/g' filename.txt
# Using vim/vi
vim '+set ff=unix' '+wq' filename.txt
- Shell scripts failing: First thing to check when a script won't run
- Configuration files: Parse errors often caused by line endings
- CSV data imports: Extra carriage returns cause parsing issues
- Git commits: Mixed line endings show as entire file changed
- Cron jobs failing: Scripts edited on Windows won't execute
- Docker builds: Windows-edited Dockerfiles fail
# On Linux/Mac (recommended)
git config --global core.autocrlf input
# This setting:
# - Converts CRLF to LF on commit
# - Leaves LF unchanged on checkout
# - Prevents Windows line endings from entering repository
# Create .gitattributes file in repository root:
echo "* text=auto" > .gitattributes
echo "*.sh text eol=lf" >> .gitattributes
echo "*.conf text eol=lf" >> .gitattributes
- Configure editors to use Unix line endings (LF only)
- Set up Git properly with autocrlf=input
- Use .gitattributes to enforce line endings
- Add dos2unix to deployment pipelines
- Set up pre-commit hooks to catch DOS line endings
- Educate team members about line ending differences
Quick Reference Card
| Task | Command |
|---|---|
| Convert single file | dos2unix filename.txt |
| Convert with backup | dos2unix -b filename.txt |
| Convert multiple files | dos2unix file1.txt file2.txt file3.txt |
| Convert to new file | dos2unix -n input.txt output.txt |
| Check file format | dos2unix -i filename.txt |
| Preserve timestamp | dos2unix -k filename.txt |
| Convert all .sh files | dos2unix *.sh |
| Recursive conversion | find . -name "*.txt" -exec dos2unix {} \; |
| Verbose output | dos2unix -v filename.txt |
| Quiet mode | dos2unix -q filename.txt |