Recipe 9.11 Working with Symbolic File Permissions Instead of Octal Values

9.11.1 Problem

You want to print, inspect, or change permissions on a file or directory, but you don't want to specify the permissions in octal (e.g., 0644, 0755). You want to print permissions as ls(1) shows them (e.g., -rwx-r-xr-x) and specify permissions changes in the way that chmod(1) does (e.g., g-w to remove write access for the group).

9.11.2 Solution

Use the CPAN module Stat::lsMode to convert numeric permissions to a string:

use Stat::lsMode;

$lsmode = file_mode($pathname);

Use the CPAN module File::chmod to manipulate symbolic permissions:

use File::chmod;

chmod("g=rw,o=-w", @files);    # group can read/write, others can't write
chmod("-rwxr-xr--", @files);   # ls-style permissions

9.11.3 Discussion

The Stat::lsMode module provides functions for generating ls-style permissions strings. The file_mode function takes a pathname and returns a permissions string. This string is false if the pathname doesn't exist or Perl can't stat it. If all goes well, you get a string like "drwxr-x---" for a directory or "-rwxr-x----" for a file. For more fine-grained control, Stat::lsMode offers format_mode, which takes a numeric permissions value and returns the 10-character ls-style string.

Notice the leading d and - in those strings. This indicates the type of file whose permissions you're inspecting: - means regular file, d means directory, l means symbolic link, and so on. The format_perms function from Stat::lsMode does the same job as format_mode, but it returns a nine-character string, which does not have the type indicator. For example:

use Stat::lsMode;
print file_mode("/etc"), "\n";
print format_mode((stat "/etc")[2]), "\n";

The File::chmod module gives you a chmod that accepts these nine-character permissions strings:

use File::chmod;
chmod("rwxr-xr-x", @files);

These strings are three clusters of three characters. The three clusters represent what the user, group, and others can do to the file (respectively). The three characters represent reading, writing, and executing, with a dash (-) in a column indicating the corresponding permission is denied to the group. So in "rwxrw-r--", the owner can read, write, and execute; users in the same group as the file can read and write but not execute; and everyone else can only read.

You can specify relative changes to the permissions for a particular file; for example, g-w removes write permission from the group. The first letter(s) indicates whose permissions are being changed (user, group, other, or a combination). Then comes a + or - to indicate adding or removing permissions, or = to indicate you're specifying the complete set of permissions. Then you specify some or all of rwx. You can join these with commas to form relative permissions; for example, g-w,o+x (remove write from group, add execute to other). If you omit the u, g, or o, then the change applies to everyone.

Here are some valid permissions changes and what they do:

u=                  remove all permissions for the user
g=r                 group can only read
g+wx                group can also write and execute
g=rwx,o=rx          group can do all, other can only read and execute
=rwx                everybody can do everything

So you can now say:

chmod("u=", @files);  # remove all permissions for the user on @files
chmod("g=r", @files);
chmod("g+wx", @files);
chmod("g=rwx,o-rx", @files);
chmod("=rwx", @files);

File::chmod also provides functions for seeing what the new permission would be without actually making the change. See the File::chmod documentation for more details.

9.11.4 See Also

The documentation for the CPAN modules File::chmod and Stat::lsMode; the chmod and stat functions in perlfunc(1)