Filehandles, files, directories
chdir#
Change the process’s current working directory.
chdir is the Perl wrapper around the chdir(2) and — where the
kernel supports it — fchdir(2) system calls. It changes the
directory that relative paths are resolved against for the current
process and, on success, persists that change for every subsequent
file operation in the program.
Synopsis#
chdir EXPR
chdir FILEHANDLE
chdir DIRHANDLE
chdir
What you get back#
1 on success, 0 on failure. On failure $! is set to the
underlying errno so you can distinguish “no such directory”
(ENOENT) from “permission denied” (EACCES) from “not a directory”
(ENOTDIR):
chdir $path
or die "chdir $path failed: $!";
The return is always a plain boolean — there is no separate “changed to nowhere because no argument was given” value. The no-argument form either succeeds by changing to the home directory or fails like any other call.
Global state it touches#
%ENV— read whenchdiris called with no argument. The lookup order is$ENV{HOME}first, then$ENV{LOGDIR}. If neither is set,chdirdoes nothing and returns0.$!— set on failure to theerrnoreported by the kernel.
chdir does not update $ENV{PWD}. Shells maintain PWD
themselves; a Perl program that cares about PWD for child processes
has to assign to $ENV{PWD} after a successful chdir.
Argument forms#
chdir EXPR—EXPRis stringified and passed tochdir(2)as a pathname. Relative paths are resolved against the current working directory at the moment of the call.chdir FILEHANDLEandchdir DIRHANDLE— on Linux (and any system withfchdir(2)) the kernel changes directory to the one the handle refers to.FILEHANDLEhere means a filehandle opened on a directory, or a directory handle fromopendir. Using the handle form avoids a TOCTOU race between resolving a pathname and changing into it.chdir(no argument) — changes to$ENV{HOME}, falling back to$ENV{LOGDIR}ifHOMEis unset. If both are unset the call fails and$!is left untouched; check the return value, not$!.
Examples#
Change into a directory and bail out if it does not exist:
chdir "/var/log"
or die "cannot enter /var/log: $!";
Run a block of work in a different directory and return to where you started:
use Cwd qw(getcwd);
my $origin = getcwd;
chdir $work_dir or die "chdir $work_dir: $!";
# ... do work relative to $work_dir ...
chdir $origin or die "chdir $origin: $!";
Change via a directory handle (race-free on systems with
fchdir(2)):
opendir my $dh, $path or die "opendir $path: $!";
chdir $dh or die "fchdir $path: $!";
Go home:
chdir or die "no HOME/LOGDIR in environment";
Probe a list of candidates, stopping at the first that works:
for my $d ("/srv/app", "/opt/app", "/usr/local/app") {
last if chdir $d;
}
Edge cases#
Empty string or
undef:chdir ""andchdir undefboth fail and set$!toENOENT. They do not fall back to$HOME— the no-argument form is triggered by absence of an argument, not by an argument that happens to be empty.Relative paths on a thread with its own cwd: the kernel’s current working directory is per-process, not per-thread, on Linux. Two threads calling
chdirconcurrently race.Symlink to a directory:
chdirfollows symlinks, landing in the target. UsereadlinkorCwd::abs_pathfirst if you need to know the real destination.Deleted directory: if the directory was removed after you entered it, relative operations from inside fail with
ENOENTand most shells show the path as(deleted). You can stillchdirout with an absolute path.Handle form on systems without
fchdir(2): raises an exception. Linux always supports it; the restriction is a portability concern for code that also runs on older systems.Untrusted input: passing a user-supplied path to
chdirwithout validation lets the caller redirect all subsequent relative-path operations in the program. Canonicalise withCwd::abs_pathand check against an allow-list before the call if the input crosses a trust boundary.After
fork: the child inherits the parent’s cwd. Achdirin the child does not affect the parent.
Differences from upstream#
Fully compatible with upstream Perl 5.42.
See also#
mkdir— create a directory; pair withchdirwhen building a workspace and stepping into itrmdir— remove a directory; do notrmdirthe cwd,chdirout firstopendir— open a directory handle usable as the argument tochdirfor race-free directory changeschroot— stronger confinement thanchdir; changes the filesystem root for the process, not just the cwd$ENV{HOME}— consulted by the no-argument form ofchdirbefore falling back to$ENV{LOGDIR}$!— carries theerrnowhenchdirreturns0