Filehandles, files, directories
chmod#
Change the permission bits of a list of files.
chmod sets each file in LIST to the permission MODE. MODE is
a numeric value — the low twelve bits are the usual Unix
permission triad plus the setuid, setgid, and sticky bits, exactly
as accepted by chmod(2).
Both plain pathnames and open filehandles are accepted in LIST.
Returns the number of files for which the underlying system call
succeeded.
Synopsis#
chmod MODE, LIST
What you get back#
The number of files successfully changed, as an integer. Zero means
every call failed; a value less than scalar @files means a partial
failure. chmod does not throw on a per-file failure — it keeps
going and tallies the successes.
$! reflects the error from the last failing file only. On a
mixed success/failure run there is no built-in way to learn which
files failed; if you need per-file error reporting, call chmod once
per file and check the return value each time:
for my $f (@files) {
chmod 0644, $f
or warn "chmod $f: $!";
}
Writing MODE correctly#
MODE is an integer. The three ways to spell it:
Octal literal with leading zero:
0644,0755,0600. This is the conventional form and matches the digits you would type intochmod(1).Octal literal with
0oprefix:0o644,0o755. Accepted since Perl 5.34; equivalent to the leading-zero form and easier to read as “octal” at a glance.Symbolic constants from
Fcntl:use Fcntl qw(:mode); S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTHevaluates to0755. Useful when a permission set is built up from pieces.
A string argument is almost always a bug:
chmod "0644", "foo"; # WRONG — stringifies as decimal 644 (octal 1204),
# grants mode --w----r-T
chmod 644, "foo"; # WRONG — decimal 644 is octal 1204, same result
chmod 0644, "foo"; # right
chmod 0o644, "foo"; # right (Perl 5.34+)
If the mode genuinely arrives as a string (configuration file, CLI
argument), convert it with oct first:
my $mode = "0644"; # string from config
chmod oct($mode), "foo"; # oct("0644") == 0644
Global state it touches#
Sets $! on any failure to match the last failing
chmod(2) /
fchmod(2) call.
Reads no interpreter globals.
Examples#
Change one file, check for success:
chmod 0755, "install.sh"
or die "chmod install.sh: $!";
Change many files in a single call, capture the count:
my $cnt = chmod 0644, "a.txt", "b.txt", "c.txt";
print "updated $cnt of 3 files\n";
With an array of pathnames:
my @executables = glob "bin/*";
chmod 0755, @executables;
Preserve existing bits, add owner write. Reads the mode with
stat, masks to the permission bits, ORs in the new bit, and
writes it back:
open my $fh, "<", "foo" or die $!;
my $perm = (stat $fh)[2] & 07777;
chmod $perm | 0200, $fh;
Symbolic form via Fcntl:
use Fcntl qw(:mode);
chmod S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, @executables;
# identical to: chmod 0755, @executables;
Per-file error reporting (needed to learn which file failed on a multi-file call):
my @failed;
for my $f (@files) {
push @failed, $f unless chmod 0644, $f;
}
warn "chmod failed on: @failed\n" if @failed;
Edge cases#
Empty list:
chmod 0644returns0. The mode is evaluated but no system call is made.String modes:
chmod "0644", $fandchmod "+x", $fare both wrong.chmodnever parses symbolic mode strings — unlikechmod(1), the built-in takes a number, not a specification.Filehandles vs barewords: a bareword in
LISTis a filename, not a filehandle. Pass a filehandle as a glob or glob reference:chmod 0644, *FH; # filehandle chmod 0644, \*FH; # filehandle chmod 0644, $fh; # filehandle (lexical) chmod 0644, "FH"; # filename "FH" — almost certainly a bug
On systems without
fchmod(2), passing a filehandle raises an exception. Linux supportsfchmod(2), so on the platforms pperl targets this always works.Setuid / setgid / sticky bits: the high three bits of the mode word (
04000,02000,01000) set setuid, setgid, and the sticky bit respectively.0o4755makes a setuid executable;0o2775a setgid directory.Symlinks:
chmodfollows symlinks. Changing permissions on the symlink itself is not portable and is not exposed by this built-in — uselchmodfrom a module if the platform supports it.Permission to change permission: only the file’s owner (and root) may call
chmod(2)on a file. Failure sets$!toEPERM.Non-existent paths:
chmodon a missing path sets$!toENOENTand contributes0to the success count. It does not warn and does not die.umask does not apply:
umaskmasks the mode of newly created files fromopen,sysopen,mkdir, and similar.chmodsets the mode literally — no masking.
Differences from upstream#
Fully compatible with upstream Perl 5.42.
See also#
chown— the sibling call for changing owner/group; same list-and-return-count shapestat— read the current mode so you can modify it rather than replace it wholesaleumask— default permission mask applied to new files; unrelated tochmod, which sets bits literallyopen/sysopen— create files; the mode argument tosysopenis masked byumaskat creation time and can then be tightened or relaxed withchmodoct— parse"0644"into the integer0644when the mode arrives as a string$!— system error from the last failing file; only meaningful for single-file calls or immediately after a failing per-file call in a loop