Working with Files and Directories
Ruby provides powerful built-in tools for working with the filesystem. Whether you’re writing deployment scripts, processing configuration files, or automating backups, Ruby’s file handling capabilities make these tasks straightforward. This tutorial covers the essential classes you’ll use daily as a DevOps engineer.
In this tutorial, you’ll learn how to:
- Read and write files using Ruby
- Navigate and manipulate directories
- Use FileUtils for common file operations
- Work with Pathnames for cross-platform path handling
Reading Files in Ruby
The simplest way to read a file in Ruby is using the File.read method. This reads the entire file contents into a string:
# Read entire file contents
content = File.read('/path/to/file.txt')
puts content
For large files, reading line by line is more memory-efficient. Ruby’s File.foreach iterates through each line without loading the whole file:
# Process file line by line
File.foreach('/path/to/file.txt') do |line|
puts line.chomp
end
You can also use a block with File.open, which automatically closes the file when the block finishes:
# File is automatically closed after the block
File.open('/path/to/file.txt', 'r') do |file|
while line = file.gets
puts line.chomp
end
end
The second argument to File.open is the mode. Common modes include:
r- Read (default)w- Write (creates new file or truncates existing)a- Appendr+- Read and writea+- Read and append
Writing Files in Ruby
Writing files follows similar patterns. Use File.write for simple one-liners:
# Write string to file (overwrites existing content)
File.write('/path/to/output.txt', 'Hello, World!')
# Append to file
File.write('/path/to/output.txt', "\nNew line", mode: 'a')
For more control, use File.open with a write mode:
# Write with explicit file handling
File.open('/path/to/output.txt', 'w') do |file|
file.puts 'First line'
file.puts 'Second line'
file.write 'Third line without newline'
end
Working with Directories
The Dir class provides directory navigation capabilities:
# List all files in a directory
Dir.entries('/path/to/directory').each do |entry|
puts entry unless entry.start_with?('.')
end
# Glob pattern matching
Dir.glob('**/*.rb').each do |file|
puts file
end
Create and delete directories:
# Create directory (and parent directories if needed)
Dir.mkdir('/path/to/new_directory')
# Create parent directories recursively
Dir.mkdir_p('/path/to/nested/directory')
# Delete directory (must be empty)
Dir.delete('/path/to/empty_directory')
Check if paths exist:
# Path existence checks
puts File.exist?('/path/to/file.txt') # true or false
puts File.directory?('/path/to/dir') # true or false
puts File.file?('/path/to/file.txt') # true or false
FileUtils: Power Tools for File Operations
The FileUtils module provides convenient methods for common operations. Require it first:
require 'fileutils'
# Copy a file
FileUtils.cp('source.txt', 'destination.txt')
# Copy directory recursively
FileUtils.cp_r('source_dir/', 'dest_dir/')
# Move or rename files
FileUtils.mv('old_name.txt', 'new_name.txt')
# Delete files
FileUtils.rm('unwanted.txt')
# Delete directory recursively
FileUtils.rm_rf('unwanted_directory/')
Create backups with timestamp:
require 'fileutils'
require 'time'
timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
backup_name = "config_#{timestamp}.yaml"
FileUtils.cp('config.yaml', backup_name)
puts "Backup created: #{backup_name}"
Pathname: Cross-Platform Path Handling
The Pathname class provides an object-oriented way to handle file paths. It handles cross-platform path separators automatically:
require 'pathname'
# Create pathname objects
path = Pathname.new('/home/user/project/file.txt')
# Get file information
puts path.basename # file.txt
puts path.dirname # /home/user/project
puts path.extname # .txt
puts path.basename # file.txt
puts path.dirname # /home/user/project
# Check path properties
puts path.exist? # true
puts path.directory? # false
puts path.file? # true
Build paths safely:
require 'pathname'
base = Pathname.new('/home/user/project')
config_dir = base + 'config' + 'environments'
puts config_dir.to_s # /home/user/project/config/environments
Pathname also handles file extensions elegantly:
require 'pathname'
path = Pathname.new('script.rb')
puts path.extname # .rb
puts path.sub_ext('.py') # script.py
puts path.basename('.rb') # script
Practical Example: Log File Processor
Here’s a practical DevOps script that processes log files:
require 'fileutils'
require 'pathname'
class LogProcessor
def initialize(log_dir, archive_dir)
@log_dir = Pathname.new(log_dir)
@archive_dir = Pathname.new(archive_dir)
end
def process
Dir.glob(@log_dir.join('*.log')).each do |log_file|
process_log(log_file)
end
end
private
def process_log(log_path)
path = Pathname.new(log_path)
puts "Processing: #{path.basename}"
error_count = 0
File.foreach(log_path) do |line|
error_count += 1 if line.include?('ERROR')
end
puts " Found #{error_count} error(s)"
# Archive processed log
archive_path = @archive_dir.join("#{path.basename}.#{Time.now.strftime('%Y%m%d')}")
FileUtils.mv(log_path, archive_path)
puts " Archived to: #{archive_path}"
end
end
# Usage
processor = LogProcessor.new('/var/logs/app', '/var/logs/archive')
processor.process
When to Use Each Approach
For simple file reading, File.read is perfect. When processing large files or needing line-by-line control, use File.foreach or File.open with a block.
Use FileUtils for operations that involve multiple steps or when you need verbose output options. The cp_r and rm_rf methods are particularly useful for directory operations.
Choose Pathname when you’re building paths from multiple components or need to extract parts of a path (extension, basename, dirname). Pathname objects are also easier to pass around in methods since they’re just objects.
Conclusion
Ruby’s filesystem libraries provide everything you need for DevOps automation. Start with the simple File.read and File.write methods, then add FileUtils and Pathname as your needs grow. These tools work seamlessly together, allowing you to build powerful scripts for file processing, backups, deployments, and more.
In the next tutorial, we’ll explore building CLI tools with Thor, which builds on these file handling concepts to create user-friendly command-line applications.