如何从命令行获取八进制文件权限?

有一个chmod命令可以设置文件权限,但是我能从命令行中以八进制模式(例如755)获取文件权限吗?

4对于像我这样无意间来到这个页面,实际上是在寻找如何在Mac上完成这个操作的人来说:https://stackoverflow.com/a/14855227/470749 - Ryan
2stat --format "%A" $FILE 给出了可读的 drwxr-x--- 格式,这可能有用也可能没有用。 - Trevor Boyd Smith
7个回答

你可以试一试
stat -c "%a %n" *

*替换为您想要检查的相关目录或确切文件名。
根据stat手册页面
-c  --format=FORMAT
          use  the  specified  FORMAT instead of the default; output a newline after
          each use of FORMAT
%a     Access rights in octal
%n     File name

使用方法:

  • 带有文件:

    $ stat -c "%a %n" ./Documents/Udev.html 
    664 ./Documents/Udev.html
    
  • 带有文件夹:

    $ stat -c "%a %n" ./Documents/
    755 ./Documents/
    

(参考)


74在Mac OS上,使用stat -f '%A %a %N' *命令(来源:http://geeklog.adamwilson.info/article/58/getting-file-permissions-in-octal-on-OS-X)来获取文件的八进制权限。 - s2t2
1如果你更喜欢使用ls命令:for f in $(ls -a); do stat -c "%a %n" $f; done; - rgajrawala
7@usandfriends 循环遍历ls的输出是一个不好的主意。如果你真的想使用循环,可以这样做:for f in *; do stat "%a %n" "$f"; done - Tom Fenech
stat -c "%a %n" `find . -true -print` 用于递归进入目录。cd /etc ; stat -c "%a %n" `find . -true -print` | while read line ; do cd /mnt/broken/etc ; chmod $line ; done 用于复制权限(例如在意外运行 chmod -R 后)。 - SF.
2为什么在Mac OS上,即使是Unix ISO工具,一切都略有不同?这背后是否有实际的原因? - Vassilis
2请 @hackel,告诉我们你真正的感受。哈哈! - MikeSchinkel
2@Vassilis回答:了解。你真正想问的问题是:“如果BSD在Linux之前发布,为什么Linux改变了标准,而不是遵循旧的UNIX系统所设定的模式?” - Scott Prive
1@hackel:在提出错误的观点之前,你本可以通过谷歌搜索GNU工具的历史来节省时间,而不是错误地暗示BSD系统选择不遵循Linux。你完全弄错了。 - Scott Prive
请注意,在zsh中,stat是一个内置的shell命令,因此请使用/usr/bin/stat -c '%a' file(指定真实stat命令的完整路径)。 - hbt
要显示隐藏文件+以颜色显示文件属性+以八进制表示权限,您可以查看我的答案,该答案仅使用简短关键字。 - MaXi32
值得一提的是,您也可以只调用stat <文件名>而不使用格式化参数(如此答案所示)。这样您将获得更多所需的输出,但在手动检查时可能并不重要,因此您无需记住确切的格式化选项。 - luator

在Linux中,可以使用Linux stat命令以八进制格式显示文件权限。
只需按下键盘上的Ctrl+Alt+T即可打开终端。打开后,导航到您想要以八进制模式查找文件权限的目录。
stat -c '%A %a %n' *

%A 以可读形式显示的访问权限

%a 八进制表示的访问权限

%n 文件名

Octal numbers and permissions

You can use octal number to represent mode/permission:

r: 4
w: 2
x: 1

For example, for file owner you can use octal mode as follows. Read, write and execute (full) permission on a file in octal is 0+r+w+x = 0+4+2+1 = 7

Only Read and write permission on a file in octal is 0+r+w+x = 0+4+2+0 = 6

Only read and execute permission on a file in octal is 0+r+w+x = 0+4+0+1 = 5

Use above method to calculate permission for group and others. Let us say you wish to give full permission to owner, read & execute permission to group, and read only permission to others, then you need to calculate permission as follows: User = r+w+x = 0+4+2+1 = 7 Group= r+w+x = 0+4+2+0 = 6 Others = r+w+x = 0+0+0+1 = 1

Effective permission is 761.

来源:http://kmaiti.blogspot.com/2011/09/umask-concept.html

2初始的0也代表特殊位:http://pastebin.com/6ymkFt71 - Braiam
可以强制输出4位数吗? - openCivilisation
1要包括以点开头的隐藏文件/文件夹(例如 _.git_),您可以使用 stat -c '%A %a %n' .* * - Sharak

“755”-style permissions with ‘ls’中所述,由Adam CourtemancheAgileAdam.com上详细介绍,您可以创建一个别名lso,它的功能类似于ls -l,但稍微处理了输出1,以便以八进制形式显示权限。这会添加一个前导列,显示三位数的八进制权限2。按照现有的写法,这对大多数文件和目录都有效,但如果设置了stickysetuid/setgid位,则无法正常工作。3
alias lso="ls -alG | awk '{k=0;for(i=0;i<=8;i++)k+=((substr(\$1,i+2,1)~/[rwx]/)*2^(8-i));if(k)printf(\" %0o \",k);print}'"

虽然techtonik 指出这有一个严重的缺陷。您无法像对ls命令那样向该lso别名传递参数,因为它们被视为awk的附加参数。因此,您无法在特定文件或目录上运行lso,也无法传递任何选项(如-F--color)给lso


解决方法是将lso定义为一个函数,而不是一个别名。
lso() { ls -alG "$@" | awk '{k=0;for(i=0;i<=8;i++)k+=((substr($1,i+2,1)~/[rwx]/)*2^(8-i));if(k)printf(" %0o ",k);print}'; }

如果您在shell中进行交互式尝试,请运行unalias lso以删除别名 - 您可以在定义函数之前或之后执行此操作。如果您将其放入源文件(例如~/.bashrc)中,只需删除alias行并添加函数定义即可。 为什么这样能行得通呢?与别名不同,bash shell函数可以接受位置参数,即命令行参数"$@"扩展为完整的参数列表,导致传递给lso函数的参数被传递给ls。(与别名定义不同,函数体不需要引号;因此,在$"之前删除\字符是必要的。)
由于在将其定义为函数时可以传递选项给lso,您可能希望从定义中删除-a-G选项--在需要时可以手动传递它们。(为了显示文件权限等详细信息,-l选项是必需的,因此删除它没有任何好处。)
lso() { ls -l "$@" | awk '{k=0;for(i=0;i<=8;i++)k+=((substr($1,i+2,1)~/[rwx]/)*2^(8-i));if(k)printf(" %0o ",k);print}'; }

感谢techtonik指出在定义lso为别名时的限制,这激励我扩展这篇文章,加入关于将其改为函数的内容。

1有人可能注意到这似乎违反了关于不解析ls输出的一般规则ls生成非常易读的输出;这引入了个别性和限制,使其通常不适合作为其他命令的输入。在这种情况下,我们解析ls,因为我们希望保留ls的确切行为,除了我们添加的一个变化。

2这个别名的一个局限性,也适用于下面显示的功能版本,并且可能被认为是一个错误,就是它只显示三个八进制数字,即使第四个八进制数字为零。

jfmercer 正如 正确指出的,这里显示的八进制数字不反映粘滞位(sticky bit)是否存在,也不反映 setuid 或 setgid 位。

3比不显示第四个八进制数字更严重的是,这种方法假设它们没有设置,并且如果设置了——如果在权限字符串中看到 tsS,那么应该忽略八进制数字。这是因为位从权限字符串推断出来的方式不考虑粘滞 setuid/setgid 位。


不适用于目录 - awk: read error (Is a directory) - anatoly techtonik
@techtonik 感谢您指出这一点!我(终于)更新了这个答案,包括了修复它的方法。 - Eliah Kagan
2你可以添加--color参数来显示颜色。lso() { ls -alG "$@" --color | awk '{k=0;for(i=0;i<=8;i++)k+=((substr($1,i+2,1)~/[rwx]/)*2^(8-i));if(k)printf(" %0o ",k);print}'; } - tagplus5
1你在发布这个问题后有没有找到解决粘性setuid/setgid问题的方法? - user428026

只是在扩展/简化之前关于“stat”相关的答案:
你可以简单地运行:
stat <path_to_file>

输出将包含八进制权限以及其他信息。
详细信息(stat版本和示例):
# stat --version
stat (GNU coreutils) 8.4


[user@localhost ~]# touch /tmp/TEST_PERMISSONS

[user@localhost ~]# chmod 644 /tmp/TEST_PERMISSONS

[user@localhost ~]# stat /tmp/TEST_PERMISSONS
  File: `/tmp/TEST_PERMISSONS'
  Size: 0           Blocks: 0          IO Block: 4096   regular empty file
Device: fd00h/64768d    Inode: 1010058     Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2015-08-26 18:58:59.000000000 +0300
Modify: 2015-08-26 18:58:59.000000000 +0300
Change: 2015-08-26 18:59:16.000000000 +0300

请注意:(0644/-rw-r--r--)

为了便携性,您可以使用perl

$ perl -e 'printf "%04o %s\n", (stat)[2] & 07777, $_ for @ARGV' *.txt
0644 1.txt
0644 2.txt
0644 3.txt
0644 4.txt
0600 PerlOneLiner.txt
0664 perl.txt

如果你想在出现错误时得到通知,请尝试以下方法:
perl -e '
for (@ARGV) {
    print "$!: $_\n" and next unless -e;
    printf "%03o %s\n", (stat)[2] & 07777, $_;
}
' *.txt

2除了便携性之外,@cuonglm的解决方案显示了四个八进制字符而不是三个,从而显示了经常被遗忘的"粘滞位(sticky bit)"的状态。 - jfmercer
如何显示隐藏文件? - MaXi32
@MaXi32 使用 .* - cuonglm

你可以使用find命令配合-printf参数。 ls命令无法显示八进制权限,但你可以使用这个基于find的解决方法。
find <em>path</em> -printf "%m:%f\n"

例如,要检查我的视频目录:
$ find Videos -printf "%m:%f\n"
755:Videos

%m 格式说明符告诉 -printf 命令打印八进制权限,而 %f 格式说明符使其打印文件名。

你可以将多个文件名传递给 find。你甚至可以使用通配符(例如:find * -printf "%m:%f\n")。

你不必使用像 -name-iname 这样的测试;只需将你感兴趣的文件或目录的名称作为起始点传递给 find 即可。也就是说,在单词 find 之后立即提供它们的名称,如上所示。

find 可以让你对输出进行精确控制。有两个特别有用的修改方式:

默认情况下,find 会递归访问子目录,类似于 ls -R。如果你不希望 find 访问传递给它的起始点的子目录,你可以添加 -maxdepth 0(或者使用其他值的 -maxdepth 来指示你希望它深入多少层)。
$ find Documents -maxdepth 0 -printf "%m:%f\n"
755:Documents
%f 只显示文件名,所以如果 find 需要递归才能找到文件,你可能不知道它的位置。为了显示一个路径,从文件所在的起始点开始,使用 %p
$ find /boot -printf "%m:%p\n"
755:/boot
644:/boot/initrd.img-4.4.0-92-generic
600:/boot/System.map-4.4.0-93-generic
600:/boot/vmlinuz-4.4.0-92-generic
600:/boot/vmlinuz-4.4.0-93-generic
....

查看man find以获取有关使用find命令的更多信息。

另一种方法(lsawk

这可以用来列出所有目录文件及其权限:

ls -l | awk '{k=0;for(i=0;i<=8;i++)k+=((substr($1,i+2,1)~/[rwx]/) \
             *2^(8-i));if(k)printf("%0o ",k);print}'

这基本上是与Adam Courtemanche的lso别名中相同的命令,那个答案引用了它,只是作为一个单独的命令运行。如果你只使用一次,或者很少使用,那么你可能不想费心将其写成别名或shell函数。

你可以使用一个类似于 "ls" 的别名显示列表。
alias lm='stat -c "%-05a %-12A %-5U %5G %s   %-015F %n" * | numfmt --to=iec --field=5 --padding=6'