Overview
FileUtils provides class methods for common file system operations across different operating systems. Ruby implements FileUtils as a module that wraps low-level file operations with consistent behavior and error handling. The module offers both destructive operations that modify the file system immediately and non-destructive preview modes for testing workflows.
FileUtils operations fall into several categories: file copying (cp
, cp_r
), moving (mv
), deletion (rm
, rm_rf
), directory creation (mkdir
, mkdir_p
), and permission changes (chmod
, chown
). Each operation supports options for verbose output, dry-run mode, and error handling behavior.
require 'fileutils'
# Copy a file
FileUtils.cp('source.txt', 'destination.txt')
# Create nested directories
FileUtils.mkdir_p('path/to/nested/directories')
# Remove files and directories
FileUtils.rm_rf('unwanted_directory')
The module defines two main operational modes: normal execution and dry-run mode. Dry-run mode prints operations without executing them, while normal mode performs actual file system changes. FileUtils also provides verbose output options that display each operation as it executes.
Ruby's FileUtils handles cross-platform differences in file operations. Path separators, permission handling, and symbolic link behavior adapt to the underlying operating system. The module raises specific exceptions for different error conditions, allowing applications to handle file operation failures appropriately.
Basic Usage
FileUtils copying operations handle both individual files and directory trees. The cp
method copies single files, while cp_r
recursively copies entire directory structures. Both methods accept arrays of source paths for batch operations.
require 'fileutils'
# Single file copy
FileUtils.cp('config.yml', 'config.yml.backup')
# Multiple file copy to directory
FileUtils.cp(['file1.txt', 'file2.txt'], 'backup_dir/')
# Recursive directory copy
FileUtils.cp_r('source_directory', 'backup_directory')
# Copy with preservation of timestamps and permissions
FileUtils.cp_r('source', 'dest', preserve: true)
Directory operations create, modify, and remove directory structures. The mkdir
method creates single directories, while mkdir_p
creates nested directory paths, similar to mkdir -p
in Unix systems. Directory removal uses rmdir
for empty directories or rm_rf
for recursive deletion.
# Create single directory
FileUtils.mkdir('new_directory')
# Create nested directory structure
FileUtils.mkdir_p('deep/nested/directory/structure')
# Remove empty directory
FileUtils.rmdir('empty_directory')
# Remove directory and all contents
FileUtils.rm_rf('directory_with_contents')
Moving and renaming operations use the mv
method, which works across file systems when necessary. The method handles both files and directories, automatically detecting whether to perform a rename operation or cross-file-system move.
# Rename file
FileUtils.mv('old_name.txt', 'new_name.txt')
# Move file to directory
FileUtils.mv('file.txt', 'destination_directory/')
# Move multiple files
FileUtils.mv(['file1.txt', 'file2.txt'], 'target_directory/')
# Move directory
FileUtils.mv('source_directory', 'new_location/')
Permission and ownership modifications apply to files and directories through chmod
and chown
operations. These methods accept both numeric and symbolic permission modes, with support for recursive application to directory trees.
# Change file permissions (numeric mode)
FileUtils.chmod(0644, 'file.txt')
# Change multiple files
FileUtils.chmod(0755, ['script1.sh', 'script2.sh'])
# Recursive permission change
FileUtils.chmod_R(0755, 'executable_directory')
# Change ownership (Unix systems)
FileUtils.chown('user', 'group', 'file.txt')
Error Handling & Debugging
FileUtils operations raise specific exception types for different failure conditions. Understanding these exceptions enables proper error recovery and user feedback. The most common exceptions include Errno::ENOENT
for missing files, Errno::EACCES
for permission errors, and Errno::ENOSPC
for insufficient disk space.
require 'fileutils'
begin
FileUtils.cp('nonexistent.txt', 'destination.txt')
rescue Errno::ENOENT => e
puts "Source file not found: #{e.message}"
rescue Errno::EACCES => e
puts "Permission denied: #{e.message}"
rescue StandardError => e
puts "Unexpected error: #{e.class} - #{e.message}"
end
FileUtils provides dry-run capabilities through the :noop
option, allowing operations to be tested without file system modifications. This mode prints the operations that would execute, making it valuable for validating complex file processing workflows before execution.
# Test operations without executing
FileUtils.cp_r('source', 'dest', noop: true, verbose: true)
# Output: cp -r source dest
# Verify directory structure before deletion
FileUtils.rm_rf('risky_directory', noop: true, verbose: true)
# Output: rm -rf risky_directory
Verbose mode provides detailed output for debugging file operations. The :verbose
option displays each operation as it executes, showing the actual commands performed. This output helps identify which operations succeed or fail in complex workflows.
# Enable verbose output for all operations
FileUtils.cp_r('source', 'dest', verbose: true)
# Output: cp -r source dest
FileUtils.mkdir_p('path/to/directory', verbose: true)
# Output: mkdir -p path/to/directory
# Combine verbose and dry-run for comprehensive testing
FileUtils.mv(Dir.glob('*.log'), 'logs/', verbose: true, noop: true)
# Output shows all files that would be moved
Permission-related errors require careful handling, especially in multi-user environments or when processing files with varied ownership. FileUtils operations respect existing file permissions, which can cause unexpected failures when copying or modifying files.
def safe_file_copy(source, dest)
# Check source file exists and is readable
unless File.readable?(source)
raise "Cannot read source file: #{source}"
end
# Check destination directory is writable
dest_dir = File.dirname(dest)
unless File.writable?(dest_dir)
raise "Cannot write to destination directory: #{dest_dir}"
end
FileUtils.cp(source, dest)
rescue Errno::ENOSPC
raise "Insufficient disk space for copy operation"
rescue Errno::EMFILE, Errno::ENFILE
raise "Too many open files - system limit reached"
end
Cross-platform path handling requires attention to path separator differences and case sensitivity. FileUtils handles separator conversion automatically, but applications must consider case sensitivity differences between file systems.
# Path joining that works across platforms
def safe_path_join(*parts)
File.join(*parts).gsub('\\', '/') # Normalize separators
end
# Case-sensitive file checking
def file_exists_case_sensitive?(path)
return false unless File.exist?(path)
# On case-insensitive systems, verify exact case match
dir = File.dirname(path)
name = File.basename(path)
Dir.entries(dir).include?(name)
end
Production Patterns
FileUtils operations in production environments require careful consideration of atomicity, error recovery, and resource management. Atomic operations ensure file system consistency during complex workflows, especially when processing critical data or configuration files.
class AtomicFileProcessor
def self.update_config(config_path, new_content)
temp_path = "#{config_path}.tmp.#{Process.pid}"
begin
# Write to temporary file first
File.write(temp_path, new_content)
# Atomic replace using move operation
FileUtils.mv(temp_path, config_path)
puts "Configuration updated successfully"
rescue StandardError => e
# Clean up temporary file on failure
FileUtils.rm_f(temp_path)
raise "Config update failed: #{e.message}"
end
end
end
Batch file processing requires progress tracking and error isolation to handle large datasets reliably. Processing files individually with proper error handling prevents single file failures from stopping entire workflows.
class BatchFileProcessor
def self.process_directory(source_dir, dest_dir, &block)
processed = 0
failed = []
Dir.glob(File.join(source_dir, '*')).each do |file_path|
next unless File.file?(file_path)
begin
filename = File.basename(file_path)
dest_path = File.join(dest_dir, filename)
# Ensure destination directory exists
FileUtils.mkdir_p(dest_dir)
# Process file with provided block
block.call(file_path, dest_path)
processed += 1
# Progress feedback for long operations
puts "Processed: #{processed}" if processed % 100 == 0
rescue StandardError => e
failed << { file: file_path, error: e.message }
puts "Failed to process #{file_path}: #{e.message}"
end
end
{ processed: processed, failed: failed }
end
end
# Usage example
result = BatchFileProcessor.process_directory('input/', 'output/') do |source, dest|
# Custom processing logic
FileUtils.cp(source, dest)
FileUtils.chmod(0644, dest)
end
Log rotation and archival workflows benefit from FileUtils operations combined with timestamp-based naming and size management. These patterns handle growing log files and maintain historical data within storage constraints.
class LogRotator
def initialize(log_file, max_size: 10_000_000, keep_files: 5)
@log_file = log_file
@max_size = max_size
@keep_files = keep_files
end
def rotate_if_needed
return unless File.exist?(@log_file)
return unless File.size(@log_file) > @max_size
timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
archived_name = "#{@log_file}.#{timestamp}"
begin
# Move current log to archive
FileUtils.mv(@log_file, archived_name)
# Create new empty log file
FileUtils.touch(@log_file)
FileUtils.chmod(0644, @log_file)
# Remove old archives beyond retention limit
cleanup_old_archives
puts "Log rotated: #{archived_name}"
rescue StandardError => e
puts "Log rotation failed: #{e.message}"
# Restore original file if move succeeded but cleanup failed
FileUtils.mv(archived_name, @log_file) if File.exist?(archived_name)
end
end
private
def cleanup_old_archives
pattern = "#{@log_file}.*"
archives = Dir.glob(pattern).sort
# Remove oldest files beyond retention limit
while archives.size > @keep_files
old_file = archives.shift
FileUtils.rm_f(old_file)
puts "Removed old archive: #{old_file}"
end
end
end
Deployment workflows use FileUtils for asset management, backup creation, and rollback capabilities. These operations require careful ordering and verification to maintain application availability during deployments.
class DeploymentManager
def initialize(app_path, backup_path)
@app_path = app_path
@backup_path = backup_path
end
def deploy(new_version_path)
backup_name = "backup_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
current_backup = File.join(@backup_path, backup_name)
begin
# Create backup directory
FileUtils.mkdir_p(@backup_path)
# Backup current version
if File.exist?(@app_path)
FileUtils.cp_r(@app_path, current_backup)
puts "Created backup: #{current_backup}"
end
# Deploy new version
FileUtils.rm_rf(@app_path) if File.exist?(@app_path)
FileUtils.cp_r(new_version_path, @app_path)
# Set proper permissions
FileUtils.chmod_R(0755, File.join(@app_path, 'bin'))
FileUtils.chmod_R(0644, File.join(@app_path, 'config'))
puts "Deployment completed successfully"
# Cleanup old backups (keep last 3)
cleanup_old_backups
rescue StandardError => e
puts "Deployment failed: #{e.message}"
rollback(current_backup) if File.exist?(current_backup)
raise
end
end
def rollback(backup_path)
puts "Rolling back to: #{backup_path}"
FileUtils.rm_rf(@app_path) if File.exist?(@app_path)
FileUtils.cp_r(backup_path, @app_path)
puts "Rollback completed"
end
private
def cleanup_old_backups
backups = Dir.glob(File.join(@backup_path, 'backup_*')).sort
while backups.size > 3
old_backup = backups.shift
FileUtils.rm_rf(old_backup)
puts "Removed old backup: #{old_backup}"
end
end
end
Reference
Core Methods
Method | Parameters | Returns | Description |
---|---|---|---|
FileUtils.cp(src, dest, **opts) |
src (String/Array), dest (String), options (Hash) |
nil |
Copy files to destination |
FileUtils.cp_r(src, dest, **opts) |
src (String/Array), dest (String), options (Hash) |
nil |
Copy files and directories recursively |
FileUtils.mv(src, dest, **opts) |
src (String/Array), dest (String), options (Hash) |
nil |
Move files and directories |
FileUtils.rm(list, **opts) |
list (String/Array), options (Hash) |
nil |
Remove files |
FileUtils.rm_r(list, **opts) |
list (String/Array), options (Hash) |
nil |
Remove files and directories recursively |
FileUtils.rm_rf(list, **opts) |
list (String/Array), options (Hash) |
nil |
Force remove files and directories recursively |
FileUtils.rm_f(list, **opts) |
list (String/Array), options (Hash) |
nil |
Force remove files (ignore missing) |
Directory Operations
Method | Parameters | Returns | Description |
---|---|---|---|
FileUtils.mkdir(list, **opts) |
list (String/Array), options (Hash) |
nil |
Create directories |
FileUtils.mkdir_p(list, **opts) |
list (String/Array), options (Hash) |
nil |
Create directories and parent directories |
FileUtils.rmdir(list, **opts) |
list (String/Array), options (Hash) |
nil |
Remove empty directories |
FileUtils.pwd |
None | String |
Return current working directory |
FileUtils.cd(dir, **opts) { } |
dir (String), options (Hash), block |
Block return value | Change directory, optionally with block |
Permission and Ownership
Method | Parameters | Returns | Description |
---|---|---|---|
FileUtils.chmod(mode, list, **opts) |
mode (Integer), list (String/Array), options (Hash) |
nil |
Change file permissions |
FileUtils.chmod_R(mode, list, **opts) |
mode (Integer), list (String/Array), options (Hash) |
nil |
Change file permissions recursively |
FileUtils.chown(user, group, list, **opts) |
user (String/Integer), group (String/Integer), list (String/Array), options (Hash) |
nil |
Change file ownership |
FileUtils.chown_R(user, group, list, **opts) |
user (String/Integer), group (String/Integer), list (String/Array), options (Hash) |
nil |
Change file ownership recursively |
File Creation and Links
Method | Parameters | Returns | Description |
---|---|---|---|
FileUtils.touch(list, **opts) |
list (String/Array), options (Hash) |
nil |
Create empty files or update timestamps |
FileUtils.ln(src, dest, **opts) |
src (String), dest (String), options (Hash) |
nil |
Create hard link |
FileUtils.ln_s(src, dest, **opts) |
src (String), dest (String), options (Hash) |
nil |
Create symbolic link |
FileUtils.ln_sf(src, dest, **opts) |
src (String), dest (String), options (Hash) |
nil |
Create symbolic link (force overwrite) |
Comparison and Information
Method | Parameters | Returns | Description |
---|---|---|---|
FileUtils.cmp(file1, file2) |
file1 (String), file2 (String) |
Boolean |
Compare file contents |
FileUtils.identical?(file1, file2) |
file1 (String), file2 (String) |
Boolean |
Check if files are identical |
FileUtils.uptodate?(file, deps) |
file (String), deps (Array) |
Boolean |
Check if file is newer than dependencies |
Common Options
Option | Type | Description | Compatible Methods |
---|---|---|---|
:noop |
Boolean |
Dry-run mode, no actual operations | All methods |
:verbose |
Boolean |
Print operations to stdout | All methods |
:force |
Boolean |
Ignore errors and continue | rm , rm_r , rm_rf , cp , cp_r |
:preserve |
Boolean |
Preserve timestamps and permissions | cp , cp_r |
:mode |
Integer |
Set permissions for created directories | mkdir , mkdir_p |
:mtime |
Time |
Set modification time | touch |
Permission Mode Values
Octal | Binary | Permissions | Description |
---|---|---|---|
0644 |
110100100 |
rw-r--r-- |
Owner read/write, group/other read |
0755 |
111101101 |
rwxr-xr-x |
Owner full, group/other read/execute |
0600 |
110000000 |
rw------- |
Owner read/write only |
0700 |
111000000 |
rwx------ |
Owner full access only |
0666 |
110110110 |
rw-rw-rw- |
All users read/write |
0777 |
111111111 |
rwxrwxrwx |
All users full access |
Exception Hierarchy
Exception Class | Description | Common Causes |
---|---|---|
Errno::ENOENT |
No such file or directory | Missing source files, invalid paths |
Errno::EACCES |
Permission denied | Insufficient permissions, read-only files |
Errno::ENOSPC |
No space left on device | Disk full, quota exceeded |
Errno::EEXIST |
File exists | Attempting to create existing file/directory |
Errno::EISDIR |
Is a directory | Using file operation on directory |
Errno::ENOTDIR |
Not a directory | Using directory operation on file |
Errno::EMFILE |
Too many open files | System file descriptor limit reached |