Skip to main content

How to Move Files in Bash

• 6 min read
bash

Quick Answer: Move Files in Bash

To move a file in Bash, use the mv command: mv source destination. Moving is just renaming within the same directory or moving to a different directory. Use mv -i for interactive mode to prevent accidental overwrites.

Quick Comparison: File Moving Methods

MethodSyntaxBest ForSafety
mvmv source destSimple movesStandard
mv -iInteractiveCareful movesSafe
mv -vVerboseSeeing progressFeedback
cp + rmTwo commandsComplex logicControlled
rsyncSync approachNetwork movesAdvanced

Bottom line: Use mv -i for safety, plain mv for scripts.



Method 1: Basic File Movement

Moving a file is simply relocating it from one directory to another while keeping its name the same. The mv command handles this with just source and destination arguments. It’s one of the fastest operations—on the same filesystem, it only updates directory entries, not copying data.

Move Single File to Directory

Here’s the fundamental pattern:

mv source.txt /backup/

This moves source.txt into the /backup/ directory, keeping its original name. The trailing slash on the directory is optional but makes your intent clear—you’re moving INTO that directory.

Move with Rename

You can move AND rename in one command:

mv /path/to/old_name.txt /backup/new_name.txt

The destination becomes both the directory AND the new filename. This is useful when you want to move and rename simultaneously.

Move Multiple Files

mv file1.txt file2.txt file3.txt /backup/

All three files move into /backup/, keeping their names. The last argument is always the destination when moving multiple files.

When to Use Basic Move

Use simple mv when:

  • Moving single or multiple files
  • You want the file to keep its name
  • Destination directory already exists
  • You’re confident about the operation
  • You want speed (very fast on same filesystem)

Avoid it when:

  • Destination might already contain the file (use -i)
  • Moving directories with many files (use caution with -r)
  • Crossing filesystems (might be slower than expected)

Method 2: Renaming Files

Renaming is just a special case of moving—moving a file within the same directory with a different name. Bash doesn’t have a separate rename command; mv handles both moving and renaming.

Simple Rename

mv old_name.txt new_name.txt

The file stays in the same directory but gets a new name. Simple and fast.

Batch Renaming

#!/bin/bash

# Rename all .txt files to .bak
for file in *.txt; do
  [ -f "$file" ] || continue
  new_name="${file%.txt}.bak"
  mv "$file" "$new_name"
  echo "Renamed: $file -> $new_name"
done

This loops through all .txt files and renames each one. The ${file%.txt} syntax removes the .txt extension, then .bak is added.

Test Batch Renaming First

Before running batch renames, always test what would be renamed:

# TEST: See what would be renamed
for file in *.txt; do
  [ -f "$file" ] || continue
  new_name="${file%.txt}.bak"
  echo "Would rename: $file -> $new_name"
done

# Then actually do it
for file in *.txt; do
  [ -f "$file" ] || continue
  new_name="${file%.txt}.bak"
  mv "$file" "$new_name"
done

This two-step approach prevents mistakes. Review the “Would rename” output before running the actual rename.

When to Use Renaming

Use mv for renaming when:

  • Changing file extensions
  • Updating naming conventions
  • Preparing files for processing
  • Removing problematic characters from names
  • Batch processing with patterns

Method 3: Interactive Move (Safety Flag)

Use the -i flag to be prompted before overwriting. This prevents accidental data loss when the destination might already exist.

Move with Confirmation

mv -i file.txt /backup/file.txt

If file.txt already exists in /backup/, you get a prompt:

mv: overwrite '/backup/file.txt'?

Respond y to overwrite or n to skip.

When Overwrite Happens

Here’s the surprise many people miss: if you’re not explicit, mv WILL overwrite without asking:

# Dangerous: overwrites silently
mv file.txt /backup/file.txt  # If file.txt exists there, it's gone

# Safe: asks first
mv -i file.txt /backup/file.txt

For critical files, always use -i or scripts should prevent overwrites explicitly.

When to Use Interactive Mode

Use -i when:

  • Overwriting could cause problems
  • You’re not 100% sure destination doesn’t have the file
  • Critical systems where overwrites are dangerous
  • Running interactively (not in automated scripts)

Don’t use it when:

  • In scripts where you want guaranteed behavior
  • Doing bulk operations (prompts get tedious)
  • You’ve already verified destination is empty

Method 4: Non-Overwrite Mode

The -n flag prevents overwriting—if the destination exists, the move is silently skipped. This is safer than interactive mode for scripts.

Move Without Overwriting

mv -n file.txt /backup/file.txt

If file.txt already exists in /backup/, the move is skipped with no error or prompt. The script continues silently.

When Non-Overwrite Matters

# Merge directories without losing existing files
mv -n /source/* /destination/

This moves everything from source to destination, but if files already exist there, they’re left alone (source isn’t overwritten).

When to Use -n Flag

Use -n when:

  • Automated scripts need predictable behavior
  • You want to preserve existing files
  • Doing incremental moves
  • You don’t want prompts in unattended scripts

Method 5: Verbose Output

The -v flag shows exactly what’s being moved. Useful for seeing progress with many files or confirming operations.

Move with Progress Display

mv -v file.txt /backup/

Output:

'file.txt' -> '/backup/file.txt'

Combine with Other Flags

# Interactive and verbose
mv -iv file.txt /backup/

# Non-overwrite and verbose
mv -nv file.txt /backup/

Flags stack together for combined behavior.

When to Use -v Flag

Use -v when:

  • Moving many files (see progress)
  • Testing rename/move operations
  • Creating logs of what happened
  • Debugging why certain files didn’t move

Practical Examples

Example 1: Organize Downloads by File Type

#!/bin/bash

DOWNLOAD_DIR="$HOME/Downloads"

# Create subdirectories if they don't exist
mkdir -p "$DOWNLOAD_DIR"/{PDFs,Images,Videos,Documents,Other}

# Organize files
for file in "$DOWNLOAD_DIR"/*; do
  # Skip if not a regular file
  [ -f "$file" ] || continue

  # Skip if already in a subdirectory
  [ "$(dirname "$file")" = "$DOWNLOAD_DIR" ] || continue

  case "$(basename "$file")" in
    *.pdf)
      mv "$file" "$DOWNLOAD_DIR/PDFs/"
      echo "Moved PDF: $(basename $file)"
      ;;
    *.jpg|*.png|*.gif|*.jpeg)
      mv "$file" "$DOWNLOAD_DIR/Images/"
      echo "Moved image: $(basename $file)"
      ;;
    *.mp4|*.mkv|*.avi|*.mov)
      mv "$file" "$DOWNLOAD_DIR/Videos/"
      echo "Moved video: $(basename $file)"
      ;;
    *.doc|*.docx|*.xlsx|*.txt)
      mv "$file" "$DOWNLOAD_DIR/Documents/"
      echo "Moved document: $(basename $file)"
      ;;
    *)
      mv "$file" "$DOWNLOAD_DIR/Other/"
      ;;
  esac
done

echo "Organization complete"

This script automatically sorts downloads into folders based on file type.

Example 2: Rename with Timestamp

#!/bin/bash

# Add timestamp to filename while keeping extension
rename_with_timestamp() {
  local file="$1"
  local timestamp=$(date +%Y%m%d_%H%M%S)
  local name="${file%.*}"      # Remove extension
  local ext="${file##*.}"      # Get extension
  local new_name="${name}_${timestamp}.${ext}"

  mv "$file" "$new_name"
  echo "Renamed: $file -> $new_name"
}

# Usage
rename_with_timestamp "report.txt"
# Result: report_20260221_143045.txt

Useful for creating dated backups before overwriting files.

Example 3: Safe Move with Verification

#!/bin/bash

safe_move() {
  local source="$1"
  local destination="$2"

  # Verify source exists
  if [ ! -e "$source" ]; then
    echo "ERROR: Source not found: $source"
    return 1
  fi

  # Check if destination exists
  if [ -e "$destination" ]; then
    read -p "Destination exists. Overwrite? (y/n) " -n 1 -r
    echo
    [[ ! $REPLY =~ ^[Yy]$ ]] && return 1
  fi

  # Ensure destination directory exists
  dest_dir=$(dirname "$destination")
  mkdir -p "$dest_dir" || return 1

  # Perform the move
  if mv -v "$source" "$destination"; then
    echo "Move successful"
    return 0
  else
    echo "Move failed"
    return 1
  fi
}

# Usage
safe_move "/tmp/file.txt" "/backup/file.txt"

This function checks everything before moving, creating directories as needed.

Example 4: Log Rotation

#!/bin/bash

rotate_log() {
  local log_file="$1"
  local archive_dir="${log_file%/*}/archive"  # Get directory, add /archive
  local timestamp=$(date +%Y%m%d_%H%M%S)

  # Create archive directory if needed
  mkdir -p "$archive_dir"

  # Move current log to archive with timestamp
  if [ -f "$log_file" ]; then
    mv "$log_file" "$archive_dir/$(basename $log_file).$timestamp"
    echo "Log rotated: $(basename $log_file)"
  fi

  # Create new empty log file with original permissions
  touch "$log_file"
  chmod 644 "$log_file"
}

# Usage
rotate_log "/var/log/myapp/app.log"

Essential for keeping log files manageable by archiving old ones.

Example 5: Conditional Move (Only if Safe)

#!/bin/bash

# Move only if destination doesn't exist
conditional_move() {
  local source="$1"
  local destination="$2"

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

  if [ -f "$destination" ]; then
    echo "SKIP: Destination already exists"
    return 2  # Different error code
  fi

  mv "$source" "$destination" && echo "Moved successfully"
}

conditional_move "/tmp/file.txt" "/backup/file.txt"

Different return codes signal different outcomes (success=0, source error=1, destination exists=2).

Example 6: Move and Deduplicate

#!/bin/bash

# Move files, skip if they already exist at destination
move_with_dedup() {
  local source_dir="$1"
  local dest_dir="$2"

  mkdir -p "$dest_dir"

  moved=0
  skipped=0

  for file in "$source_dir"/*; do
    [ -f "$file" ] || continue

    filename=$(basename "$file")
    if [ -f "$dest_dir/$filename" ]; then
      echo "SKIP (exists): $filename"
      ((skipped++))
    else
      mv "$file" "$dest_dir/"
      echo "MOVED: $filename"
      ((moved++))
    fi
  done

  echo "Completed: $moved moved, $skipped skipped"
}

move_with_dedup "/tmp/upload" "/home/archive"

Moves files while avoiding overwriting existing ones, keeping track of what happened.

Quick Reference

# Simple move
mv file.txt /backup/

# Rename
mv old.txt new.txt

# Move and rename
mv /old/path/old.txt /new/path/new.txt

# Interactive (ask before overwrite)
mv -i file.txt /backup/

# Verbose (show what's moving)
mv -v file.txt /backup/

# Don't overwrite
mv -n file.txt /backup/

# Combined flags
mv -inv file.txt /backup/

# Move multiple files
mv file1.txt file2.txt file3.txt /backup/

# Move all of a type
mv *.log /archive/

# Batch rename extension
for f in *.txt; do mv "$f" "${f%.txt}.bak"; done

Important Considerations

Filesystem Boundaries

Moving on the same filesystem is instant (just updates directory entries). But moving across filesystems actually copies, then deletes—much slower:

# Fast (same filesystem)
mv /home/user/file.txt /home/user/backup/

# Slow (different filesystem—actually copies and deletes)
mv /home/file.txt /mnt/usb/file.txt

Always Quote Filenames

# CORRECT: Handles spaces
mv "$old_file" "$new_file"

# WRONG: Breaks with spaces
mv $old_file $new_file  # Fails if names have spaces

Exit Codes Matter in Scripts

if mv "$source" "$destination"; then
  echo "Success"
else
  echo "Failed (exit code: $?)"
fi

Success is exit code 0, failure is non-zero.

Summary

The mv command is your workhorse for file organization and renaming. Use plain mv when you’re confident, -i for interactive safety, and -n to prevent overwrites in scripts. Remember that renaming is just a special case of moving within the same directory. Always test batch operations before running them on real files, and be aware that moving across filesystems is slower than moving within the same filesystem.

#!/bin/bash

DOWNLOAD_DIR="/home/user/Downloads"

# Organize files by type
for file in "$DOWNLOAD_DIR"/*; do
  [ -f "$file" ] || continue

  case "$file" in
    *.pdf)
      mv "$file" "$DOWNLOAD_DIR/PDFs/"
      ;;
    *.jpg|*.png|*.gif)
      mv "$file" "$DOWNLOAD_DIR/Images/"
      ;;
    *.mp4|*.mkv|*.avi)
      mv "$file" "$DOWNLOAD_DIR/Videos/"
      ;;
    *)
      # Unknown type
      mv "$file" "$DOWNLOAD_DIR/Other/"
      ;;
  esac
done

Batch Renaming

Rename multiple files with a pattern:

#!/bin/bash

# Rename all .txt files to .bak
for file in *.txt; do
  [ -f "$file" ] || continue
  new_name="${file%.txt}.bak"
  mv "$file" "$new_name"
  echo "Renamed: $file -> $new_name"
done

# Output:
# Renamed: document1.txt -> document1.bak
# Renamed: document2.txt -> document2.bak

Renaming with Timestamps

Add timestamps to files:

#!/bin/bash

TIMESTAMP=$(date +%Y%m%d_%H%M%S)

# Rename file with timestamp
mv important_file.txt "important_file_${TIMESTAMP}.txt"

# Result: important_file_20260221_143045.txt

Safe Move Function

Create a safe function that checks before moving:

#!/bin/bash

safe_move() {
  local source="$1"
  local dest="$2"

  # Check if source exists
  if [ ! -e "$source" ]; then
    echo "ERROR: Source does not exist: $source"
    return 1
  fi

  # Check if destination exists
  if [ -e "$dest" ]; then
    read -p "Destination exists. Overwrite? (y/n) " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
      echo "Move cancelled"
      return 1
    fi
  fi

  # Perform move
  mv "$source" "$dest"
  echo "Moved: $source -> $dest"
  return 0
}

# Usage
safe_move "/tmp/file.txt" "/backup/file.txt"

Moving and Renaming Together

Move a file to a new location with a new name:

#!/bin/bash

# Move and rename in one command
mv /home/user/old_project /home/archive/new_project_name

# Move file to directory and rename it
mv document.txt /archive/document_final.txt

Conditional Move

Move only if destination doesn’t exist:

#!/bin/bash

source="/tmp/myfile.txt"
dest="/backup/myfile.txt"

if [ ! -f "$dest" ]; then
  mv "$source" "$dest"
  echo "File moved successfully"
else
  echo "Destination already exists, skipping move"
fi

Practical Example: Log Rotation

#!/bin/bash

LOG_DIR="/var/log/myapp"
ARCHIVE_DIR="/var/log/myapp/archive"

# Move current log to archive and add timestamp
if [ -f "$LOG_DIR/app.log" ]; then
  TIMESTAMP=$(date +%Y%m%d_%H%M%S)
  mv "$LOG_DIR/app.log" "$ARCHIVE_DIR/app_$TIMESTAMP.log"

  # Create new empty log file
  touch "$LOG_DIR/app.log"

  echo "Log rotated at $(date)"
fi

Important Notes

  • mv on the same filesystem is fast (just updates directory entries)
  • mv across filesystems might copy and delete (slower)
  • Use -f to force move without prompting (dangerous!)
  • Always test with echo first if renaming many files
  • Don’t forget quotes around filenames with spaces: mv "old name.txt" "new name.txt"
  • Exit code is 0 on success, non-zero on failure

Quick Reference

# Basic move
mv file.txt /backup/

# Rename
mv old.txt new.txt

# Interactive (ask before overwrite)
mv -i file.txt /backup/

# Verbose (show what's moving)
mv -v file.txt /backup/

# Don't overwrite
mv -n file.txt /backup/

# Move and rename together
mv /old/path/old_name.txt /new/path/new_name.txt

# Move multiple files
mv file1.txt file2.txt file3.txt /backup/

# Move all files of a type
mv *.log /backup/

Summary

The mv command is essential for file management. Use it to move files between directories and rename them. Remember to use -i for safety when overwriting and test your commands before running them on multiple files.