fmt [OPTION]... [FILE]...
fmt [-WIDTH] [FILE]...
The fmt command reformats text to fit within a specified line width, breaking long lines and filling short ones to create uniform paragraph formatting. It's the Unix equivalent of a word processor's "reflow" function - taking ragged, irregular text and making it neat and readable at a specified width (default 75 characters).
Originally designed for formatting email and text files before sophisticated word processors existed, fmt remains invaluable for cleaning up text documentation, formatting README files, preparing text for email clients, and wrapping code comments. It intelligently preserves paragraph breaks, handles indentation, and can maintain uniform spacing between words, making it perfect for automated text cleanup in scripts and pipelines.
# Create sample text with irregular line lengths
cat > messy.txt << 'EOF'
This is a very long line that extends way beyond what would be considered reasonable for reading on most displays and should definitely be wrapped to a more manageable width.
This is short.
Another really long line here that goes on and on and on without any breaks which makes it difficult to read especially in terminals with limited width or in email clients.
This is a new paragraph.
It also has some long lines that need formatting.
EOF
# Basic fmt - default 75 character width
fmt messy.txt
# Format and save to new file
fmt messy.txt > formatted.txt
# Format from stdin
cat messy.txt | fmt
# Format multiple files
fmt file1.txt file2.txt file3.txt
# View before and after
echo "=== BEFORE ==="
cat messy.txt
echo ""
echo "=== AFTER ==="
fmt messy.txt
This is a very long line that extends way beyond what would be
considered reasonable for reading on most displays and should definitely
be wrapped to a more manageable width. This is short. Another really
long line here that goes on and on and on without any breaks which makes
it difficult to read especially in terminals with limited width or in
email clients.
This is a new paragraph. It also has some long lines that need
formatting.
fmt joins short lines within a paragraph but breaks long ones. Blank lines delimit paragraphs - each paragraph is formatted independently. Default width is 75 characters, suitable for most email clients and terminals.
# Create sample text
cat > sample.txt << 'EOF'
The quick brown fox jumps over the lazy dog. This sentence is repeated multiple times to demonstrate how fmt wraps text at different widths.
The quick brown fox jumps over the lazy dog again and again.
EOF
# Format to 40 characters (narrow, good for email)
echo "=== 40 characters ==="
fmt -w 40 sample.txt
# Format to 60 characters
echo "=== 60 characters ==="
fmt -w 60 sample.txt
# Format to 100 characters (wide, good for documentation)
echo "=== 100 characters ==="
fmt -w 100 sample.txt
# Shorthand notation (no -w needed)
echo "=== Using shorthand -50 ==="
fmt -50 sample.txt
# Very narrow (useful for side-by-side comparisons)
echo "=== 30 characters ==="
fmt -30 sample.txt
# Practical: format for 80-column terminal
fmt -80 README.txt > README.formatted.txt
# Format git commit message to standard 72 chars
git log -1 --pretty=%B | fmt -72
=== 40 characters ===
The quick brown fox jumps over the
lazy dog. This sentence is repeated
multiple times to demonstrate how
fmt wraps text at different widths.
The quick brown fox jumps over the
lazy dog again and again.
=== 60 characters ===
The quick brown fox jumps over the lazy dog. This sentence
is repeated multiple times to demonstrate how fmt wraps
text at different widths. The quick brown fox jumps over
the lazy dog again and again.
=== 100 characters ===
The quick brown fox jumps over the lazy dog. This sentence is repeated multiple times to demonstrate
how fmt wraps text at different widths. The quick brown fox jumps over the lazy dog again and
again.
=== 30 characters ===
The quick brown fox jumps
over the lazy dog. This
sentence is repeated
multiple times to
demonstrate how fmt wraps
text at different widths.
The quick brown fox jumps
over the lazy dog again and
again.
Common widths: 72 for git commits, 75-80 for general text, 60 for email, 100+ for documentation. The shorthand fmt -WIDTH is equivalent to fmt -w WIDTH and is more commonly used.
# Create text with irregular spacing
cat > spacing.txt << 'EOF'
This has irregular spacing. Multiple spaces between words.
See? Also after periods.Here's another sentence.And another.
EOF
# Default fmt (preserves some spacing irregularities)
echo "=== Default formatting ==="
fmt spacing.txt
# Uniform spacing
echo "=== With -u (uniform) ==="
fmt -u spacing.txt
# Compare side by side
echo "=== Comparison ==="
echo "Without -u:"
fmt spacing.txt
echo ""
echo "With -u:"
fmt -u spacing.txt
# Practical: clean up pasted text with weird spacing
pbpaste | fmt -u -w 72 | pbcopy # macOS
# xclip -o | fmt -u -w 72 | xclip -i # Linux
# Format email text with proper spacing
cat email_draft.txt | fmt -u -60 > email_formatted.txt
=== Default formatting ===
This has irregular spacing. Multiple spaces between words.
See? Also after periods.Here's another sentence.And another.
=== With -u (uniform) ===
This has irregular spacing. Multiple spaces between words. See? Also
after periods. Here's another sentence. And another.
=== Comparison ===
Without -u:
This has irregular spacing. Multiple spaces between words.
See? Also after periods.Here's another sentence.And another.
With -u:
This has irregular spacing. Multiple spaces between words. See? Also
after periods. Here's another sentence. And another.
The -u flag enforces classic typography: one space between words, two after sentence-ending punctuation (. ! ?). This makes text more readable and professionally formatted. Especially useful for cleaning up text copied from various sources.
# Create indented text
cat > indented.txt << 'EOF'
First level paragraph with some text that needs to be formatted.
Second level indented paragraph that also has some long text that should be wrapped while preserving the indentation.
Third level even more indented text here that definitely needs wrapping.
Different indent paragraph with just two spaces instead of four.
EOF
# Without preserving indentation (flattens everything)
echo "=== Without -p ==="
fmt -60 indented.txt
# Preserve indentation with -p
echo "=== With -p (preserve indentation) ==="
fmt -p -60 indented.txt
# Tagged paragraphs with -t (crown margin)
cat > tagged.txt << 'EOF'
1. This is the first item in a numbered list that has text long enough to demonstrate how fmt handles crown margins.
2. This is the second item with similarly long text.
3. Third item here.
EOF
echo "=== Tagged with -t ==="
fmt -t -60 tagged.txt
# Practical: format email replies preserving > quotes
cat > email_reply.txt << 'EOF'
> Original message here that was sent to me and I'm replying to it with some commentary.
> The original goes on for a while.
My response to the above message with my own lengthy commentary that needs formatting.
EOF
fmt -p '>' -60 email_reply.txt
# Format code comments preserving indentation
cat > code_comments.c << 'EOF'
/* This is a long comment that extends beyond reasonable width
and should be reformatted while maintaining indentation */
// Another comment style that's too long for the screen
EOF
fmt -p -80 code_comments.c
=== Without -p ===
First level paragraph with some text that needs to be
formatted.
Second level indented paragraph that also has some long text
that should be wrapped while preserving the indentation.
Third level even more indented text here that definitely needs
wrapping.
Different indent paragraph with just two spaces instead of
four.
=== With -p (preserve indentation) ===
First level paragraph with some text that needs to be
formatted.
Second level indented paragraph that also has some long
text that should be wrapped while preserving the
indentation.
Third level even more indented text here that
definitely needs wrapping.
Different indent paragraph with just two spaces instead of
four.
=== Tagged with -t ===
1. This is the first item in a numbered list that has text
long enough to demonstrate how fmt handles crown margins.
2. This is the second item with similarly long text.
3. Third item here.
The -p flag preserves indentation throughout the paragraph. The -t flag creates "crown margin" style where the first line indent differs from subsequent lines - perfect for numbered lists and bullet points. Use -p 'PREFIX' to preserve lines starting with PREFIX (like email quotes).
# Create multi-paragraph document
cat > document.txt << 'EOF'
INTRODUCTION
This is the introduction paragraph. It contains some important information that extends across multiple lines and needs proper formatting for readability.
METHODOLOGY
The methodology section describes our approach. This paragraph also has text that is quite long and irregular, with some lines being very long and others quite short.
Like this one.
And this.
RESULTS
Our results show significant findings. The data indicates a clear trend across all measurements taken during the study period.
CONCLUSION
In conclusion, we can state with confidence that the hypothesis was supported by the data. Further research is needed to explore additional dimensions of this phenomenon.
EOF
# Format the entire document
fmt -70 document.txt
# Save formatted version
fmt -70 document.txt > document_formatted.txt
# Format with uniform spacing
fmt -u -70 document.txt
# Compare paragraph handling
echo "=== Original paragraph structure ==="
grep -c '^$' document.txt
echo "blank lines"
echo "=== After formatting ==="
fmt -70 document.txt | grep -c '^$'
echo "blank lines (preserved)"
# Practical: format markdown file preserving structure
cat README.md | fmt -80 > README.formatted.md
# Format but preserve multiple blank lines
awk 'BEGIN{blank=0}
/^$/ {blank++; if(blank<=2) print; next}
{blank=0; print}' document.txt | fmt -70
INTRODUCTION
This is the introduction paragraph. It contains some important
information that extends across multiple lines and needs proper
formatting for readability.
METHODOLOGY
The methodology section describes our approach. This paragraph
also has text that is quite long and irregular, with some lines
being very long and others quite short. Like this one. And this.
RESULTS
Our results show significant findings. The data indicates a clear
trend across all measurements taken during the study period.
CONCLUSION
In conclusion, we can state with confidence that the hypothesis
was supported by the data. Further research is needed to explore
additional dimensions of this phenomenon.
fmt preserves blank lines between paragraphs but merges lines within paragraphs. Multiple consecutive blank lines are reduced to single blank lines. Section headers that are separate paragraphs remain separate. This maintains document structure while improving readability.
# Create email with quoted text
cat > email.txt << 'EOF'
Hi John,
> Thanks for your email. I wanted to respond to your question about the project timeline and deliverables. Here's my understanding of where we are.
I appreciate your response. However, I think we need to reconsider the timeline because of the recent changes to the requirements.
> > The original deadline was set for December 15th based on the initial scope.
> >
> > Can we still meet that deadline?
> I believe we can if we prioritize the core features and defer the nice-to-haves to a later phase.
That sounds reasonable. Let me know what you think about moving forward with this approach.
Thanks,
Craig
EOF
# Format email preserving quote markers
fmt -w 72 email.txt
# Better: preserve indentation for quotes
fmt -p '>' -w 72 email.txt
# Format with uniform spacing
fmt -u -p '>' -w 72 email.txt
# Handle multiple quote levels
cat > complex_email.txt << 'EOF'
> Original sender wrote:
> > Previous person said something
> > that was quite lengthy
>
> First reply adding more text
> that needs formatting
My response here
EOF
fmt -p '>' -w 60 complex_email.txt
# Practical: format email before sending
compose_email() {
cat > /tmp/email_draft.txt << 'EOF'
Your email content here...
EOF
# Format and display
fmt -u -p '>' -w 72 /tmp/email_draft.txt
}
# Clean up pasted email
pbpaste | fmt -u -p '>' -w 72 | pbcopy
Hi John,
> Thanks for your email. I wanted to respond to your question
> about the project timeline and deliverables. Here's my
> understanding of where we are.
I appreciate your response. However, I think we need to reconsider
the timeline because of the recent changes to the requirements.
> > The original deadline was set for December 15th based on
> > the initial scope.
> >
> > Can we still meet that deadline?
> I believe we can if we prioritize the core features and defer
> the nice-to-haves to a later phase.
That sounds reasonable. Let me know what you think about moving
forward with this approach.
Thanks, Craig
Email quote markers (>) are preserved with -p '>'. Multiple quote levels (>>) are handled correctly. Standard email width is 72 characters for compatibility with various email clients. Use -u for clean, consistent spacing.
# Create code with long comments
cat > sample.py << 'EOF'
# This is a really long comment that extends way beyond the recommended 79 character limit for Python code and really should be wrapped to multiple lines for better readability
def process_data(input_file, output_file):
"""This docstring is also quite long and extends beyond reasonable width making it hard to read in standard editor windows or when viewing code on GitHub"""
pass
# Another long comment here that talks about the implementation details and why we chose this particular approach for handling edge cases
EOF
# Format Python comments (preserve # and indentation)
awk '/^#/ || /^ #/ || /^"""/ {print; next} {print}' sample.py | \
fmt -p '#' -w 79
# Format C-style comments
cat > sample.c << 'EOF'
/* This is a very long C comment that extends beyond the typical 80 character line limit and should be wrapped for better readability in the source code */
int main() {
// Inline comment that is also quite long and extends past reasonable width for code review
return 0;
}
EOF
# Preserve /* and // markers
sed 's|^/\*|/\* |' sample.c | fmt -w 78 | sed 's|/\* |/\*|'
# Format shell script comments
cat > script.sh << 'EOF'
#!/bin/bash
# This is a long comment in a shell script that explains what the script does and why it was written this way and what assumptions it makes about the environment
echo "Hello"
EOF
fmt -p '#' -w 78 script.sh
# Practical: format all comments in a file
format_comments() {
local file="$1"
local width="${2:-79}"
case "$file" in
*.py)
fmt -p '#' -w "$width" "$file"
;;
*.sh)
fmt -p '#' -w "$width" "$file"
;;
*.c|*.cpp)
# Handle C-style comments
fmt -w "$width" "$file"
;;
*)
echo "Unknown file type"
return 1
;;
esac
}
format_comments sample.py 79
# Python formatted:
# This is a really long comment that extends way beyond the
# recommended 79 character limit for Python code and really should be
# wrapped to multiple lines for better readability
def process_data(input_file, output_file):
"""This docstring is also quite long and extends beyond
reasonable width making it hard to read in standard editor
windows or when viewing code on GitHub"""
pass
# Another long comment here that talks about the implementation
# details and why we chose this particular approach for handling
# edge cases
# C formatted:
/* This is a very long C comment that extends beyond the typical 80
character line limit and should be wrapped for better readability
in the source code */
int main() {
// Inline comment that is also quite long and extends past
// reasonable width for code review
return 0;
}
Be careful when reformatting code files. fmt doesn't understand code syntax and might break strings or other code elements. Use it selectively on comments, not entire code files. Consider using language-specific formatters for comprehensive code formatting.
# Extract and format specific sections
cat document.txt | \
awk '/RESULTS/,/CONCLUSION/ {print}' | \
fmt -70
# Format git commit messages
git log --pretty=format:"%B" -1 | fmt -72
# Format man page output to narrow width
man bash | col -b | fmt -60 | less
# Clean up and format pasted text
# macOS:
pbpaste | fmt -u -w 72 | pbcopy
# Linux:
xclip -o | fmt -u -w 72 | xclip -i
# Format extracted quotes
grep "^> " email.txt | \
sed 's/^> //' | \
fmt -60 | \
sed 's/^/> /'
# Create formatted documentation from code
grep -h "^#" *.sh | \
sed 's/^# //' | \
fmt -u -80 > script_documentation.txt
# Format SQL comments
cat query.sql | \
grep "^--" | \
sed 's/^-- //' | \
fmt -70 | \
sed 's/^/-- /'
# Combine multiple text files and format
cat intro.txt body.txt conclusion.txt | \
fmt -u -75 > complete_document.txt
# Format stdin with automatic line numbering
fmt -60 input.txt | nl -ba
# Create formatted README from template
cat README.template | \
envsubst | \
fmt -u -80 > README.md
# Format and preview
fmt -70 document.txt | \
pr -h "Formatted Document" | \
less
# Git commit formatted to 72 chars:
Add new feature for data processing
This commit introduces a new data processing feature that
significantly improves performance when handling large datasets.
The implementation uses a streaming approach to reduce memory
usage.
# Formatted man page (narrow for side-by-side viewing):
BASH(1) General Commands Manual
NAME
bash - GNU Bourne-Again SHell
SYNOPSIS
bash [options] [command_string | file]
# Formatted quotes:
> Thanks for the detailed explanation. I understand
> the approach now and think we should proceed with
> this implementation.
# Documentation from scripts:
This script processes log files and generates reports.
It takes an input directory and output file as
arguments. The script handles various log formats
including Apache and nginx.
fmt works seamlessly in pipelines. Common patterns: extract text with grep/awk, clean with sed, format with fmt. Remember fmt reads stdin when no file is given, making it perfect for pipeline usage. Combine with tee to save intermediate results.
# Create config with long comments
cat > app.conf << 'EOF'
# Database configuration section - This section contains all database-related settings including connection strings, timeouts, and pool sizes
database.host=localhost
database.port=5432
# Cache configuration - These settings control how the application caches data and for how long cached entries remain valid before expiration
cache.ttl=3600
cache.max_size=1000
# Logging configuration - Control verbosity and output destinations for application logs including file rotation policies
log.level=INFO
log.file=/var/log/app.log
EOF
# Format only comments
awk '/^#/ {comment=$0; getline; print comment; print}
!/^#/ {print}' app.conf | \
fmt -p '#' -w 70
# Format README sections
cat > README.txt << 'EOF'
# Installation
To install this application you need to first download the source code from the repository then run the installation script which will compile the code and install all necessary dependencies.
# Configuration
The application can be configured through the main configuration file located in /etc/app/config.yml where you can set various parameters.
# Usage
Run the application with the --help flag to see all available options and their descriptions.
EOF
# Format keeping headers intact
awk '/^# / {print; next}
/^$/ {print; next}
{para=para $0 " "; next}
END {if (para) print para}' README.txt | \
fmt -w 72
# Practical: format Dockerfile comments
cat > Dockerfile << 'EOF'
FROM ubuntu:20.04
# Install system dependencies - This includes all the base packages needed for building and running the application including compilers and libraries
RUN apt-get update && apt-get install -y build-essential
# Copy application files and set up the working directory for the application to run from
COPY . /app
WORKDIR /app
EOF
fmt -p '#' -w 75 Dockerfile
# Format INI file comments
cat > settings.ini << 'EOF'
[General]
; This is the general configuration section where you can set application-wide parameters that affect overall behavior
app_name=MyApp
version=1.0.0
[Database]
; Database connection settings including host, port, credentials and connection pooling parameters for optimal performance
host=localhost
port=5432
EOF
fmt -p ';' -w 70 settings.ini
# Format YAML comments
cat > config.yml << 'EOF'
# Server configuration
# This section defines how the server should behave including which port to listen on and what hostname to use for external connections
server:
port: 8080
host: 0.0.0.0
# Security settings
# Configure authentication and authorization including session timeouts and encryption parameters
security:
session_timeout: 3600
EOF
fmt -p '#' -w 70 config.yml
# Formatted config:
# Database configuration section - This section contains all
# database-related settings including connection strings,
# timeouts, and pool sizes
database.host=localhost
database.port=5432
# Cache configuration - These settings control how the
# application caches data and for how long cached entries remain
# valid before expiration
cache.ttl=3600
cache.max_size=1000
# Formatted README:
# Installation
To install this application you need to first download the
source code from the repository then run the installation script
which will compile the code and install all necessary
dependencies.
# Configuration
The application can be configured through the main configuration
file located in /etc/app/config.yml where you can set various
parameters.
# Formatted Dockerfile:
FROM ubuntu:20.04
# Install system dependencies - This includes all the base
# packages needed for building and running the application
# including compilers and libraries
RUN apt-get update && apt-get install -y build-essential
When formatting config files, use -p to preserve comment markers (#, ;, //). Be careful not to format actual config values - only comments and documentation. Consider using awk to selectively format only comment lines while leaving config directives unchanged.
#!/bin/bash
# File: format_docs.sh
# Automated document formatting with fmt
set -euo pipefail
# ============================================
# Configuration
# ============================================
readonly DEFAULT_WIDTH=75
readonly DEFAULT_OPTIONS="-u"
readonly BACKUP_SUFFIX=".bak"
readonly LOG_FILE="format_docs.log"
# ============================================
# Logging
# ============================================
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
# ============================================
# File Type Detection
# ============================================
detect_comment_prefix() {
local file="$1"
local ext="${file##*.}"
case "$ext" in
py|sh|bash|yaml|yml|rb)
echo "#"
;;
c|cpp|h|hpp|java|js|ts|go)
echo "//"
;;
ini|conf)
echo ";"
;;
*)
echo ""
;;
esac
}
# ============================================
# Formatting Functions
# ============================================
format_file() {
local file="$1"
local width="${2:-$DEFAULT_WIDTH}"
local preserve_indent="${3:-no}"
local uniform="${4:-yes}"
log "Formatting: $file (width: $width)"
# Detect if file has structure that needs preservation
local comment_prefix=$(detect_comment_prefix "$file")
# Build fmt command
local fmt_cmd="fmt"
# Add width
fmt_cmd="$fmt_cmd -w $width"
# Add uniform spacing if requested
if [ "$uniform" = "yes" ]; then
fmt_cmd="$fmt_cmd -u"
fi
# Add prefix preservation if detected
if [ -n "$comment_prefix" ]; then
fmt_cmd="$fmt_cmd -p '$comment_prefix'"
elif [ "$preserve_indent" = "yes" ]; then
fmt_cmd="$fmt_cmd -p"
fi
# Create backup
cp "$file" "${file}${BACKUP_SUFFIX}"
# Format file
eval "$fmt_cmd" "$file" > "${file}.tmp"
# Check if formatting produced valid output
if [ -s "${file}.tmp" ]; then
mv "${file}.tmp" "$file"
log " Success: $file formatted"
return 0
else
log " Error: Formatting produced empty file, restoring backup"
mv "${file}${BACKUP_SUFFIX}" "$file"
rm -f "${file}.tmp"
return 1
fi
}
# ============================================
# Quality Checks
# ============================================
check_line_lengths() {
local file="$1"
local max_width="${2:-$DEFAULT_WIDTH}"
local longest=$(awk '{print length}' "$file" | sort -rn | head -1)
if [ "$longest" -le "$max_width" ]; then
log " ✓ All lines within $max_width characters (longest: $longest)"
return 0
else
log " ⚠ Some lines exceed $max_width characters (longest: $longest)"
return 1
fi
}
check_paragraph_structure() {
local file="$1"
local para_count=$(awk 'BEGIN{para=0}
/^$/{para++}
END{print para}' "$file")
log " Paragraphs: $para_count"
}
# ============================================
# Batch Processing
# ============================================
format_directory() {
local dir="$1"
local width="${2:-$DEFAULT_WIDTH}"
local pattern="${3:-*.txt}"
log "========================================="
log "Batch formatting: $dir"
log "Pattern: $pattern"
log "Width: $width"
log "========================================="
local total=0
local success=0
local failed=0
while IFS= read -r -d '' file; do
((total++))
if format_file "$file" "$width"; then
((success++))
check_line_lengths "$file" "$width"
else
((failed++))
fi
done < (find "$dir" -name "$pattern" -type f -print0)
log "========================================="
log "Summary:"
log " Total files: $total"
log " Successful: $success"
log " Failed: $failed"
log "========================================="
}
# ============================================
# Report Generation
# ============================================
generate_report() {
local dir="$1"
local report_file="formatting_report.txt"
{
echo "Document Formatting Report"
echo "Generated: $(date)"
echo "Directory: $dir"
echo ""
echo "Files Processed:"
echo "================"
find "$dir" -name "*.txt" -type f | while read -r file; do
local lines=$(wc -l < "$file")
local longest=$(awk '{print length}' "$file" | sort -rn | head -1)
local words=$(wc -w < "$file")
printf "%-40s %5d lines %3d max %6d words\n" \
"$(basename "$file")" "$lines" "$longest" "$words"
done
echo ""
echo "Formatting Statistics:"
echo "====================="
awk 'BEGIN{total=0; success=0; failed=0}
/Formatting:/ {total++}
/Success:/ {success++}
/Error:/ {failed++}
END {
print "Total files processed:", total
print "Successfully formatted:", success
print "Failed:", failed
if (total > 0) {
printf "Success rate: %.1f%%\n", (success/total)*100
}
}' "$LOG_FILE"
} > "$report_file"
cat "$report_file"
log "Report saved to: $report_file"
}
# ============================================
# Main
# ============================================
usage() {
cat << EOF
Usage: $(basename "$0") [OPTIONS] [FILE|DIRECTORY]
Options:
-w WIDTH Line width (default: $DEFAULT_WIDTH)
-p Preserve indentation
-d Directory mode (process all .txt files)
-r Generate report
-h Show this help
Examples:
$(basename "$0") document.txt
$(basename "$0") -w 80 document.txt
$(basename "$0") -d -w 72 /path/to/docs
$(basename "$0") -r /path/to/docs
EOF
}
main() {
local width=$DEFAULT_WIDTH
local preserve_indent="no"
local directory_mode="no"
local generate_report_flag="no"
# Parse options
while getopts "w:pdrh" opt; do
case $opt in
w) width="$OPTARG" ;;
p) preserve_indent="yes" ;;
d) directory_mode="yes" ;;
r) generate_report_flag="yes" ;;
h) usage; exit 0 ;;
*) usage; exit 1 ;;
esac
done
shift $((OPTIND-1))
if [ $# -eq 0 ]; then
usage
exit 1
fi
local target="$1"
# Initialize log
> "$LOG_FILE"
if [ "$generate_report_flag" = "yes" ]; then
generate_report "$target"
elif [ "$directory_mode" = "yes" ]; then
format_directory "$target" "$width"
elif [ -f "$target" ]; then
format_file "$target" "$width" "$preserve_indent"
check_line_lengths "$target" "$width"
check_paragraph_structure "$target"
else
log "Error: $target is not a file or directory"
exit 1
fi
}
main "$@"
# Format single file:
$ ./format_docs.sh document.txt
[2025-12-14 17:00:00] Formatting: document.txt (width: 75)
[2025-12-14 17:00:00] Success: document.txt formatted
[2025-12-14 17:00:00] ✓ All lines within 75 characters (longest: 74)
[2025-12-14 17:00:00] Paragraphs: 5
# Batch process directory:
$ ./format_docs.sh -d -w 80 /docs
[2025-12-14 17:01:00] =========================================
[2025-12-14 17:01:00] Batch formatting: /docs
[2025-12-14 17:01:00] Pattern: *.txt
[2025-12-14 17:01:00] Width: 80
[2025-12-14 17:01:00] =========================================
[2025-12-14 17:01:00] Formatting: intro.txt (width: 80)
[2025-12-14 17:01:00] Success: intro.txt formatted
[2025-12-14 17:01:00] Formatting: body.txt (width: 80)
[2025-12-14 17:01:00] Success: body.txt formatted
[2025-12-14 17:01:01] =========================================
[2025-12-14 17:01:01] Summary:
[2025-12-14 17:01:01] Total files: 15
[2025-12-14 17:01:01] Successful: 15
[2025-12-14 17:01:01] Failed: 0
[2025-12-14 17:01:01] =========================================
# Generate report:
Document Formatting Report
Generated: Sun Dec 14 17:02:00 EST 2025
Directory: /docs
Files Processed:
================
intro.txt 45 lines 78 max 234 words
body.txt 123 lines 80 max 789 words
Formatting Statistics:
=====================
Total files processed: 15
Successfully formatted: 15
Failed: 0
Success rate: 100.0%
This production script handles batch processing, automatic file type detection, backup creation, quality checks, and reporting. It's suitable for formatting large documentation sets, maintaining consistent formatting standards, and automated document processing workflows.
fmt -u for professional output. It enforces uniform spacing: one space
between words, two after sentences. Makes text more readable and typographically correct.
fmt -p '>' -w 72 to preserve quote markers. This maintains
thread context while reformatting quoted text for readability.
fmt file.txt | less to preview,
or fmt file.txt > file.new to create a new file. fmt doesn't modify files in-place.
grep '^#' file | fmt -w 70
-g flag to set goal width. fmt tries to reach goal before max width, creating
more evenly filled lines. Example: fmt -w 80 -g 70
fmt -t for numbered lists and bullet points. Creates "crown margin" where first
line indentation differs from continuation lines.