您问题中的模式对应于具有 644 权限的常规文件(所有者为读写,其他所有人为只读),但不要相信我的话。
$ touch foo
$ chmod 644 富
$ perl -le '打印 +(stat "foo")[2]'
33188
$mode 的值可以看成一个十进制整数,但这样做并不是特别有启发性。看到八进制表示会让人更加熟悉。
$ perl -e 'printf "%o\n", (stat "foo")[2]'
100644
按位与 07777 给出数字二进制表示的最后 12 位。对于 Unix 模式,此操作提供权限或模式位并丢弃任何类型信息。
$ perl -e 'printf "%d\n", (stat "foo")[2] & 07777' # 十进制,没用
420
$ perl -e 'printf "%o\n", (stat "foo")[2] & 07777' # 八进制,尤里卡!
644
下面是一个更好的方法。请继续阅读以了解所有详细信息。
模式位
从stat(对应于struct stat 中的st_mode)返回的第三个元素是bit field,其中不同的位位置是二进制标志。
例如,st_mode POSIX 名称 S_IWUSR 中的一位。模式设置了该位的文件或目录可由其所有者写入。一个相关的位是S_IROTH,设置后意味着其他用户(即,既不是所有者也不是组中的用户)可以读取该特定文件或目录。
perlfunc documentation for stat 给出了常用模式位的名称。我们可以检查它们的值。
#! /usr/bin/env perl
use strict;
use warnings;
use Fcntl ':mode';
my $perldoc_f_stat = q(
# Permissions: read, write, execute, for user, group, others.
S_IRWXU S_IRUSR S_IWUSR S_IXUSR
S_IRWXG S_IRGRP S_IWGRP S_IXGRP
S_IRWXO S_IROTH S_IWOTH S_IXOTH
# Setuid/Setgid/Stickiness/SaveText.
# Note that the exact meaning of these is system dependent.
S_ISUID S_ISGID S_ISVTX S_ISTXT
# File types. Not necessarily all are available on your system.
S_IFREG S_IFDIR S_IFLNK S_IFBLK S_IFCHR S_IFIFO S_IFSOCK S_IFWHT S_ENFMT
);
my %mask;
foreach my $sym ($perldoc_f_stat =~ /\b(S_I\w+)\b/g) {
my $val = eval { no strict 'refs'; &$sym() };
if (defined $val) {
$mask{$sym} = $val;
}
else {
printf "%-10s - undefined\n", $sym;
}
}
my @descending = sort { $mask{$b} <=> $mask{$a} } keys %mask;
printf "%-10s - %9o\n", $_, $mask{$_} for @descending;
在 Red Hat Enterprise Linux 和 System V 系列中的其他操作系统上,上述程序的输出将是
S_ISTXT - 未定义
S_IFWHT - 未定义
S_IFSOCK - 140000
S_IFLNK - 120000
S_IFREG - 100000
S_IFBLK - 60000
S_IFDIR - 40000
S_IFCHR - 20000
S_IFIFO - 10000
S_ISUID - 4000
S_ISGID - 2000
S_ISVTX - 1000
S_IRWXU - 700
S_IRUSR - 400
S_IWUSR - 200
S_IXUSR - 100
S_IRWXG - 70
S_IRGRP - 40
S_IWGRP - 20
S_IXGRP - 10
S_IRWXO - 7
S_IROTH - 4
S_IWOTH - 2
S_IXOTH - 1
有点玩弄
上面的数字是八进制(以 8 为底),所以任何给定的数字必须是 0-7 并且位值是 8n,其中 n em> 是小数点左侧从零开始的位数。要查看它们如何映射到位,八进制具有每个数字对应于三个位的便利属性。四、二和 1 都是 2 的精确幂,因此在二进制中,它们分别是 100、10 和 1。二进制中的七(= 4 + 2 + 1)是 111,所以 708 是 1110002。后一个示例显示了来回转换是多么简单。
有了一个位域,你并不关心什么位在那个位置的值是什么,但是它是零还是非零,所以
if ($mode & $mask) {
测试$mode 中对应$mask 的任何位是否已设置。举个简单的例子,给定 4 位整数 1011 和掩码 0100,它们的按位与为
1011
& 0100
------
0000
所以那个位置的位是清晰的——而不是掩码,比如 0010 或 1100。
清除 1011 的最高位看起来像
1011 1011
& ~(1000) = & 0111
------
0011
回想一下,Perl 中的~ 是按位补码。
为了完整起见,使用按位或设置位
$bits |= $mask;
八进制和文件权限
八进制数字直接映射到三位对于 Unix 权限来说很方便,因为它们以三个一组的形式出现。例如,产生上述输出的程序的权限是
-rwxr-xr-x 1 gbacon users 1096 Feb 24 20:34 modebits
即所有者可以读、写和执行;但其他人都可以阅读和执行。在八进制中,这是 755——一个紧凑的速记。就上表而言,模式中的设置位为
S_IRUSR
S_IWUSR
S_IXUSR
S_IRGRP
S_IXGRP
S_IROTH
S_IXOTH
我们可以通过在上面的程序中添加几行来分解您的问题中的模式。
my $mode = 33188;
print "\nBits set in mode $mode:\n";
foreach my $sym (@descending) {
if (($mode & $mask{$sym}) == $mask{$sym}) {
print " - $sym\n";
$mode &= ~$mask{$sym};
}
}
printf "extra bits: %o\n", $mode if $mode;
模式测试必须更加小心,因为某些掩码是多个位的简写。当设置了一些位但不是全部时,测试我们得到准确的掩码可以避免误报。
该循环还会清除所有检测到的命中中的位,因此最后我们可以检查我们是否考虑了每个位。输出是
在模式 33188 中设置的位:
- S_IFREG
- S_IRUSR
- S_IWUSR
- S_IRGRP
- S_IROTH
没有额外的警告,所以我们得到了一切。
那个魔法 07777
将 77778 转换为二进制得到0b111_111_111_111。回想一下,78 是 1112,四个 7 对应 4×3 个。此掩码对于选择最后 12 个中的设置位很有用。回顾我们之前生成的位掩码
S_ISUID - 4000
S_ISGID - 2000
S_ISVTX - 1000
S_IRWXU - 700
S_IRWXG - 70
S_IRWXO - 7
我们看到最后 9 位是用户、组和其他的权限。前面的三位是 setuid、setgroupid 和有时称为粘滞位的东西。比如我系统上sendmail的完整模式是-rwxr-sr-x或者3428510。按位 AND 的结果是
(dec) (oct) (bin)
34285 102755 1000010111101101
& 4095 = & 7777 = & 111111111111
------- -------- ------------------
1517 = 2755 = 10111101101
模式中被丢弃的高位是S_IFREG,表明它是一个普通文件。请注意,以八进制表示的模式与以十进制或二进制表示的相同信息相比要清晰得多。
stat documentation 提到了一个有用的功能。
… 和 S_IF* 函数是
S_IMODE($mode)
$mode 中包含权限位和 setuid/setgid/sticky 位的部分
In ext/Fcntl/Fcntl.xs,我们在最后一行找到了它的实现和一个熟悉的常量。
void
S_IMODE(...)
PREINIT:
dXSTARG;
SV *mode;
PPCODE:
if (items > 0)
mode = ST(0);
else {
mode = &PL_sv_undef;
EXTEND(SP, 1);
}
PUSHu(SvUV(mode) & 07777);
为了避免magic numbers在源代码中的不良做法,写
my $permissions = S_IMODE $mode;
使用S_IMODE 和 Fcntl 模块中可用的其他功能也隐藏了低级位旋转并专注于程序想要的域级信息。文档继续
S_IFMT($mode)
$mode 中包含文件类型的部分,可以使用(例如)S_IFREG 或以下函数进行位与运算
# The operators -f, -d, -l, -b, -c, -p, and -S.
S_ISREG($mode) S_ISDIR($mode) S_ISLNK($mode)
S_ISBLK($mode) S_ISCHR($mode) S_ISFIFO($mode) S_ISSOCK($mode)
# No direct -X operator counterpart, but for the first one
# the -g operator is often equivalent. The ENFMT stands for
# record flocking enforcement, a platform-dependent feature.
S_ISENFMT($mode) S_ISWHT($mode)
使用这些常量和函数将通过更直接地表达您的意图使您的程序更清晰。