Working Directories and File Paths
Use getwd(), setwd(), and here() for reliable cross-platform paths.
Working Directories and File Paths is a free R Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the R Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What Is the Working Directory?
The working directory is the folder R uses as a starting point when you reference files with relative paths. It is the default location R looks in (and writes to) when you do not provide a full path. Every R session has exactly one working directory at any time.
# getwd() returns the current working directory
current_dir <- getwd()
cat('Working directory:', current_dir)
# All relative file references start from this location:
# read.csv('data.csv') <- looks in current_dir/data.csv
# source('R/helpers.R') <- looks in current_dir/R/helpers.Rsetwd() — Changing the Working Directory
setwd() changes the working directory for the current R session. It returns the previous working directory invisibly, which is useful for restoring state. Note: setwd() in scripts is discouraged in project-based workflows.
# Set a new working directory
old_dir <- setwd('/tmp')
cat('Now in:', getwd(), '\n')
# Restore original directory
setwd(old_dir)
cat('Restored to:', getwd(), '\n')
# Common anti-pattern (breaks on other machines):
# setwd('/Users/alice/Desktop/my_project') <- hardcoded!
# Better: use RStudio Projects which set wd automaticallyfile.path() — Building Portable Paths
file.path() constructs file paths by joining components with the correct separator for the operating system (/ on Mac/Linux, \ on Windows). Always use it instead of string concatenation for cross-platform compatibility.
# Build a path from components
data_path <- file.path('data', 'raw', 'sales.csv')
cat('Path:', data_path)
# On Unix: data/raw/sales.csv
# On Windows: data\raw\sales.csv
# Multiple levels
output_file <- file.path('output', '2024', 'Q1', 'report.pdf')
cat('Output:', output_file)
# Combine with getwd() for a full absolute path
full_path <- file.path(getwd(), 'data', 'sales.csv')
cat('Full path:', full_path)normalizePath() — Resolving to Absolute Path
normalizePath() converts a relative path to its canonical absolute form, resolving . (current dir) and .. (parent dir) components. This is essential when you need to store or log an unambiguous file location.
# Resolve relative paths to absolute
abs_path <- normalizePath('.')
cat('Absolute cwd:', abs_path, '\n')
# Resolve a relative path
data_abs <- normalizePath(file.path('..', 'data', 'sales.csv'),
mustWork = FALSE)
cat('Resolved:', data_abs, '\n')
# mustWork = FALSE: do not error if file does not exist yet
# mustWork = TRUE (default): error if path does not exist
# Useful for building reliable log messages or config files
cat('normalizePath demo complete')dirname() and basename()
dirname() extracts the directory portion of a path, and basename() extracts the file name portion. These are the R equivalents of Unix dirname and basename shell commands.
path <- '/Users/alice/projects/analysis/data/sales_2024.csv'
# Extract the directory
folder <- dirname(path)
cat('Directory:', folder, '\n')
# /Users/alice/projects/analysis/data
# Extract the file name
filename <- basename(path)
cat('File name:', filename, '\n')
# sales_2024.csv
# Remove file extension
name_only <- tools::file_path_sans_ext(filename)
cat('Name only:', name_only)file.exists() — Checking Before Reading
Always check whether a file exists before trying to read it to avoid cryptic error messages. file.exists() returns TRUE or FALSE and works on both files and directories.
data_file <- file.path('data', 'sales.csv')
# Check existence before reading
if (file.exists(data_file)) {
cat('File found, loading...\n')
# df <- read.csv(data_file)
} else {
cat('File not found:', data_file, '\n')
stop('Cannot proceed without data file.')
}
# Check multiple files at once
files <- c('data/a.csv', 'data/b.csv', 'data/c.csv')
exists_vec <- file.exists(files)
cat('Files found:', sum(exists_vec), 'of', length(files))Sys.getenv() — Environment Variable Paths
Environment variables store system-level paths like the home directory, temporary folder, and custom app paths. Sys.getenv() reads these values, making your code adaptable to different systems without hardcoding paths.
# Get the home directory
home_dir <- Sys.getenv('HOME')
cat('Home:', home_dir, '\n')
# Get the temp directory
tmp_dir <- Sys.getenv('TMPDIR')
cat('Temp:', tmp_dir, '\n')
# Custom environment variable (set in .Renviron)
data_root <- Sys.getenv('DATA_ROOT', unset = '/default/data')
cat('Data root:', data_root, '\n')
# Use env vars to build portable paths
config_file <- file.path(home_dir, '.config', 'myapp', 'settings.json')
cat('Config path:', config_file)path.expand() — Tilde Expansion
The tilde ~ is shorthand for the home directory on Unix-like systems, but R's file functions do not always expand it automatically. path.expand() replaces ~ with the actual home directory path.
# Expand tilde to full home path
short_path <- '~/.Rprofile'
full_path <- path.expand(short_path)
cat('Expanded:', full_path)
# e.g. /Users/alice/.Rprofile
# Useful when passing paths to external tools or logging
config_dir <- path.expand('~/.config/R')
cat('Config dir:', config_dir)
# Can expand multiple paths at once
paths <- c('~/data', '~/output', '~/scripts')
expanded <- path.expand(paths)
cat(expanded, sep = '\n')Listing Files with list.files()
list.files() (alias dir()) lists files in a directory, optionally filtered by a pattern and optionally returning full paths. It is indispensable when you need to process multiple files in a folder.
# List all files in current directory
all_files <- list.files('.')
cat('Files found:', length(all_files), '\n')
# Filter by extension
csv_files <- list.files('data', pattern = '\\.csv$', full.names = TRUE)
cat('CSV files:', length(csv_files), '\n')
# Recursive: include subdirectories
r_files <- list.files('R', pattern = '\\.R$',
full.names = TRUE, recursive = TRUE)
cat('R scripts:', length(r_files))Creating and Removing Paths
R has built-in functions to create directories and manage files. Use dir.create() to make new folders (with recursive = TRUE to create nested paths), and file.remove() to clean up temporary files.
# Create a directory (will not error if it already exists)
dir.create('output/figures', recursive = TRUE, showWarnings = FALSE)
cat('Directory created\n')
# Create a temporary file
tmp_file <- tempfile(fileext = '.csv')
write.csv(data.frame(x = 1:3), tmp_file, row.names = FALSE)
cat('Temp file:', tmp_file, '\n')
# Remove the file when done
if (file.remove(tmp_file)) {
cat('Temp file cleaned up')
}here Package — Project-Relative Paths
The here package solves the working directory problem elegantly. here::here() always builds paths relative to the project root (where the .Rproj file lives), regardless of where in the project you call it from.
# install.packages('here')
# library(here)
# Always resolves from project root:
# here('data', 'raw', 'sales.csv')
# here('R', 'helpers.R')
# here('output', 'report.html')
# Without here: setwd() headaches when script is in a subfolder
# With here: same path works from any subfolder
# Demonstrate base-R equivalent approach:
project_root <- normalizePath(file.path(getwd(), '..'), mustWork = FALSE)
data_path <- file.path(project_root, 'data', 'sales.csv')
cat('Data path:', data_path)Quick Check
You have a path string 'data/raw/../clean/sales.csv'. Which function would you use to resolve this into a clean, canonical absolute path?
File Paths — Key Takeaways
Robust file path handling is essential for reproducible R scripts:
getwd()/setwd()— get and set working directoryfile.path('a', 'b', 'c.csv')— OS-portable path constructionnormalizePath(path, mustWork = FALSE)— resolve to absolutedirname()/basename()— split path into folder and filefile.exists()— check before readingSys.getenv('HOME')— access system paths via env varspath.expand('~')— expand tilde to home dirlist.files(pattern = '\.csv$')— find files by extensiondir.create(recursive = TRUE)— safely make nested dirs- Use here package for project-relative paths in RStudio projects
# Robust file loading pattern:
data_file <- file.path('data', 'raw', 'sales.csv')
if (!file.exists(data_file)) {
stop(paste('Missing file:', normalizePath(data_file, mustWork = FALSE)))
}
# df <- read.csv(data_file)
cat('File path:', data_file, '\n')
cat('Full path:', normalizePath(data_file, mustWork = FALSE))Frequently asked questions
Is the “Working Directories and File Paths” lesson free?
Yes — the full text of “Working Directories and File Paths” is free to read here on the web, and the R Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the R Academy course, upgrade to CoddyKit PRO.
What will I learn in “Working Directories and File Paths”?
Use getwd(), setwd(), and here() for reliable cross-platform paths. You practise R Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start R Academy?
No prior experience is required. R Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Working Directories and File Paths” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this R Academy lesson?
Yes. Every R Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Using source() to Load Scripts
- Comments, Style, and Readability
- Working Directories and File Paths
- R Projects and Workspace Management