从stat函数输出中了解和解码文件模式值

我一直在试图了解下面提到的代码究竟发生了什么。 但我无法理解。

$mode = (stat($filename))[2]; printf "Permissions are %04o\n", $mode & 07777; 

比方说我的$模式值是33188

$ mode&07777产生一个值= 420

  • $ mode值是十进制数吗?

  • 为什么我们要select07777,为什么我们要做一点点操作。 我无法理解这里的逻辑。

Solutions Collecting From Web of "从stat函数输出中了解和解码文件模式值"

从你的问题的模式对应于一个普通的文件与644权限(读者为所有者和只读为其他人),但不要听我的话。

  $ touch foo
 $ chmod 644 foo
 $ perl -le'print +(stat“foo”)[2]'
 33188 

$mode的值可以看作是一个十进制整数,但这样做并不是特别有启发性。 看到八进制表示给了一些更熟悉的东西。

  $ perl -e'printf“%o \ n”,(stat“foo”)[2]'
 100644 

按位AND与07777给出数字的二进制表示的最后十二位。 在Unix模式下,该操作提供权限或模式位,并丢弃任何类型信息。

  $ perl -e'printf“%d \ n”,(stat“foo”)[2]&07777'#decimal,not useful
 420
 $ perl -e'printf“%o \ n”,(stat“foo”)[2]&07777'#octal,eureka!
 644 

下面是一个更好的方法。 阅读所有的细节。


模式位

stat返回的第三个元素(对应于struct stat st_mode )是一个位字段 ,其中不同的位位置是二进制标志。

例如, st_mode POSIX中的st_mode名称为S_IWUSR 。 模式有此位的文件或目录可由其所有者写入。 一个相关的位是S_IROTH ,当设置意味着其他用户( 既不是所有者也不在组中)可以读取特定的文件或目录。

stat的perlfunc文档给出了常用模式位的名称。 我们可以检查他们的价值。

 #! /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 

位twiddling

上面的数字是八进制数(基数为8),所以给定的数字必须是0-7,并且具有地址值8n,其中n是小数点左边的从零开始的位置数。 要看他们如何映射到位,八进制有一个方便的属性,每个数字对应三位。 四,二,一是两个精确的幂,所以在二进制中,它们分别是100,10和1。 二进制中的七(= 4 + 2 + 1)是111,那么70 8就是111000 2 。 后面的例子显示了如何直接转换。

用一个位域,你并不关心这个位置的值是多少 ,而是零或非零,

 if ($mode & $mask) { 

测试是否设置了与$mask对应的$mode任何位。 举一个简单的例子,给定4位整数1011和一个掩码0100,它们按位AND

  1011 & 0100 ------ 0000 

所以这个位置的位是清楚的,而不是像0010或1100那样的掩码。

清除1011的最重要的位看起来像

  1011 1011 & ~(1000) = & 0111 ------ 0011 

回想一下Perl中的~是按位补充的。

为了完整性,在位中设置一个或多个位

 $bits |= $mask; 

八进制和文件权限

一个八进制数字的直接映射到三位对Unix权限是很方便的,因为它们是三个一组的。 例如,产生上面输出的程序的权限是

  -rwxr-xr-x 1 gbacon用户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

将7777 8转换为二进制给出0b111_111_111_111 。 回想一下,7 8是111 2,4 7是4×3。 这个掩码对于选择最近十二个设置的位是有用的。 回顾一下我们之前生成的位掩码

  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或34285 10 。 按位与事实是

  (dec) (oct) (bin) 34285 102755 1000010111101101 & 4095 = & 7777 = & 111111111111 ------- -------- ------------------ 1517 = 2755 = 10111101101 

被丢弃的模式中的高位是S_IFREG ,它是一个常规文件的标志。 注意与十进制或二进制相同的信息相比,以八进制表示的模式有多清楚。

stat文档提到了一个有用的功能。

…和S_IF*功能

S_IMODE($mode)
$mode部分包含权限位和setuid / setgid / sticky位

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); 

为了避免在源代码中不好的练习魔法数字 ,写

 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) 

使用这些常量和函数将使您的程序更清晰,更直接地表达您的意图。

这是在perldoc -f stat中解释的,在这里我假定你找到了这个例子:

 Because the mode contains both the file type and its permissions, you should mask off the file type portion and (s)printf using a "%o" if you want to see the real permissions. 

printf "%04o", 420的输出是printf "%04o", 420这是你的文件的权限。 420只是八进制数0644的十进制表示形式。

如果您尝试以二进制形式打印数字,则更容易看出:

 perl -lwe 'printf "%016b\n", 33188' 1000000110100100 perl -lwe 'printf "%016b\n", 33188 & 07777' 0000000110100100 

正如你会注意到的,按位移除上面的数字中最左边的位,这可能代表文件类型,只留下文件权限。 这个数字07777是二进制数字:

 perl -lwe 'printf "%016b\n", 07777' 0000111111111111 

它在按位and按钮中扮演着“掩码”的角色。 由于1&1 = 1且0&1 = 0,这意味着任何07777中与1不匹配的位都被设置为0。