Skip to main content

How to Rename File Extensions

β€’ 2 min read
bash

Quick Answer: Rename File Extensions in Bash

To rename a file extension, use mv: mv file.txt file.md. For batch renaming, use a loop with parameter expansion: for f in *.txt; do mv "$f" "${f%.txt}.md"; done. The ${f%.txt} syntax removes the old extension.

Quick Comparison: File Extension Renaming Methods

MethodSyntaxBatchBest For
mvmv file.old file.newSingleOne file
for loop + mvfor f in *.old; do mv ... doneYesSimple batch
rename commandrename 's/\.old$/.new/' *.oldYesBulk rename
sed + xargsls *.old | sed ...YesPiped renaming
find + execfind . -name "*.old" -exec ...YesComplex patterns

Bottom line: Use loops for simple batch renaming, use rename for complex patterns.


Rename file extensions in batch using bash parameter expansion, loops, and tools like rename. Learn single and bulk renaming.

Simple Extension Rename with mv

# Rename single file
mv file.txt file.md

# Using parameter expansion for extension
file="document.txt"
newfile="${file%.txt}.md"
mv "$file" "$newfile"

Rename Multiple Files

# Rename all .txt files to .md
for file in *.txt; do
  mv "$file" "${file%.txt}.md"
done

This removes .txt and adds .md to each file.

Detailed Example with Output

Test files:

report.txt
notes.txt
summary.txt
# Rename all .txt to .md
for file in *.txt; do
  newfile="${file%.txt}.md"
  mv "$file" "$newfile"
  echo "Renamed: $file β†’ $newfile"
done

Output:

Renamed: report.txt β†’ report.md
Renamed: notes.txt β†’ notes.md
Renamed: summary.txt β†’ summary.md

Parameter Expansion for Extensions

#!/bin/bash

file="document.pdf"

# Remove extension (everything from last dot)
name="${file%.*}"
echo "Name: $name"           # Output: document

# Get just extension
extension="${file##*.}"
echo "Extension: $extension"  # Output: pdf

# Replace extension
new_extension="docx"
newfile="${name}.${new_extension}"
echo "New file: $newfile"    # Output: document.docx

Function for Extension Renaming

#!/bin/bash

rename_extension() {
  local file="$1"
  local old_ext="$2"
  local new_ext="$3"

  if [ ! -f "$file" ]; then
    echo "ERROR: File not found: $file"
    return 1
  fi

  # Check if file has the old extension
  if [[ ! "$file" =~ \.$old_ext$ ]]; then
    echo "ERROR: File doesn't end with .$old_ext"
    return 1
  fi

  # Get basename without extension
  name="${file%.*}"

  # Create new filename
  newfile="${name}.${new_ext}"

  # Perform rename
  if mv "$file" "$newfile"; then
    echo "βœ“ Renamed: $file β†’ $newfile"
  else
    echo "βœ— Failed to rename: $file"
    return 1
  fi
}

# Usage
rename_extension "document.txt" "txt" "md"

Batch Rename in Directory

#!/bin/bash

# File: batch_rename.sh

directory="${1:-.}"
old_ext="${2:-.txt}"
new_ext="${3:-.md}"

if [ ! -d "$directory" ]; then
  echo "ERROR: Directory not found: $directory"
  exit 1
fi

count=0

for file in "$directory"/*$old_ext; do
  if [ -f "$file" ]; then
    # Get directory and filename
    dir=$(dirname "$file")
    basename=$(basename "$file")
    name="${basename%.*}"

    newfile="$dir/${name}.${new_ext}"

    if mv "$file" "$newfile"; then
      echo "βœ“ $basename β†’ $(basename $newfile)"
      ((count++))
    fi
  fi
done

echo ""
echo "Renamed $count files"

Usage:

$ chmod +x batch_rename.sh
$ ./batch_rename.sh . txt md

Output:

βœ“ file1.txt β†’ file1.md
βœ“ file2.txt β†’ file2.md
βœ“ file3.txt β†’ file3.md

Renamed 3 files

Using the β€˜rename’ Tool

# If rename command is available
rename 's/\.txt$/.md/' *.txt

# Rename with regex pattern
rename 's/old_name/new_name/' old_name.*

# Safely preview changes (dry-run)
rename -n 's/\.txt$/.md/' *.txt

# Create backups of renamed files
rename -b 's/\.txt$/.md/' *.txt

Rename with Conditions

#!/bin/bash

# Only rename files smaller than 1MB

directory="."
max_size=$((1024 * 1024))  # 1MB

for file in "$directory"/*.txt; do
  if [ -f "$file" ]; then
    size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null)

    if [ "$size" -lt "$max_size" ]; then
      newfile="${file%.txt}.bak"
      mv "$file" "$newfile"
      echo "Backed up: $(basename $file) β†’ $(basename $newfile)"
    fi
  fi
done

Case Conversion in Extension

#!/bin/bash

# Convert extension to lowercase

for file in *; do
  if [ -f "$file" ]; then
    # Get name and extension
    name="${file%.*}"
    ext="${file##*.}"

    # Convert to lowercase
    lower_ext=$(echo "$ext" | tr '[:upper:]' '[:lower:]')

    # Rename if different
    if [ "$ext" != "$lower_ext" ]; then
      newfile="${name}.${lower_ext}"
      mv "$file" "$newfile"
      echo "Changed: $file β†’ $newfile"
    fi
  fi
done

Example (before):

Document.TXT
Image.JPEG
Data.CSV

Example (after):

Document.txt
Image.jpeg
Data.csv

Safe Rename with Backup

#!/bin/bash

# Rename with automatic backup creation

file="$1"
old_ext="$2"
new_ext="$3"

if [ ! -f "$file" ]; then
  echo "ERROR: File not found"
  exit 1
fi

# Create backup
cp "$file" "${file}.backup"
echo "Backup created: ${file}.backup"

# Rename
newfile="${file%.$old_ext}.$new_ext"
mv "$file" "$newfile"

echo "Renamed: $file β†’ $newfile"
echo "Original backup: ${file}.backup"

Rename with Logging

#!/bin/bash

# Log all rename operations

log_file="rename_log.txt"
old_ext="$1"
new_ext="$2"

{
  echo "=== Rename Operation ==="
  echo "Date: $(date)"
  echo "From: .$old_ext β†’ .$new_ext"
  echo ""
} >> "$log_file"

for file in *$old_ext; do
  if [ -f "$file" ]; then
    newfile="${file%.*}.${new_ext}"
    mv "$file" "$newfile"

    echo "$file β†’ $newfile" >> "$log_file"
  fi
done

echo "Log saved to: $log_file"

Recursive Rename in Subdirectories

#!/bin/bash

# Rename files in all subdirectories

old_ext="$1"
new_ext="$2"

if [ -z "$old_ext" ] || [ -z "$new_ext" ]; then
  echo "Usage: $0 <old_ext> <new_ext>"
  exit 1
fi

find . -type f -name "*$old_ext" | while read file; do
  newfile="${file%.*}.${new_ext}"
  mv "$file" "$newfile"
  echo "Renamed: $file"
done

echo "βœ“ Batch rename complete"

Error Handling and Validation

#!/bin/bash

rename_with_validation() {
  local file="$1"
  local new_ext="$2"

  # Validation
  if [ ! -f "$file" ]; then
    echo "ERROR: File not found: $file"
    return 1
  fi

  if [ -z "$new_ext" ]; then
    echo "ERROR: No extension provided"
    return 1
  fi

  # Create new filename
  name="${file%.*}"
  newfile="${name}.${new_ext}"

  # Check if target already exists
  if [ -f "$newfile" ]; then
    echo "ERROR: Target file already exists: $newfile"
    return 1
  fi

  # Perform rename
  if mv "$file" "$newfile" 2>/dev/null; then
    echo "βœ“ Renamed: $file β†’ $newfile"
    return 0
  else
    echo "βœ— Failed to rename: $file"
    return 1
  fi
}

# Usage
rename_with_validation "document.txt" "md"

Common Mistakes

  1. Forgetting quotes - fails with spaces: mv $file $newfile vs mv "$file" "$newfile"
  2. Wrong parameter expansion - ${file%.txt} removes from right, ${file#*/} from left
  3. Not checking if file exists - rename fails silently
  4. Case sensitivity - .TXT β‰  .txt
  5. Hidden files - remember to handle dotfiles

Performance Tips

  • Test with a few files first
  • Use dry-run before actual rename
  • Keep backups of important files
  • Log operations for auditing
  • Consider using find for recursive operations

Key Points

  • Use ${file%.*} to get name without extension
  • Use ${file##*.} to get just extension
  • Always quote variables in loops
  • Test with small dataset first
  • Keep backups of important files

Summary

Renaming file extensions in bulk is straightforward with bash parameter expansion. Always validate input, test carefully, and keep backups. The rename tool offers more features if available on your system.