将ls -l输出格式转换为chmod格式


17

说我有以下输出ls -l

drwxr-xr-x 2 root root 4096 Apr  7 17:21 foo

如何将其自动转换为所使用的格式chmod

例如:

$ echo drwxr-xr-x | chmod-format
755

我正在使用OS X 10.8.3。


2
使用容易得多stat。你有吗?(这是一个GNU工具,因此大多数在Linux上可用,而在Unix上则不可用。)
manatwork 2013年

@manatwork stat foo16777219 377266 drwxr-xr-x 119 Tyilo staff 0 4046 "Apr 7 17:49:03 2013" "Apr 7 18:08:31 2013" "Apr 7 18:08:31 2013" "Nov 25 17:13:52 2012" 4096 0 0 /Users/Tyilo。我看不到755它。
Tyilo

Answers:


24

某些系统具有将文件的权限显示为数字的命令,但不幸的是,这些文件不可移植。

zsh在模块中内置了stat(aka zstatstat

zmodload zsh/stat
stat -H s some-file

然后,处于mode$s[mode]但处于模式,即类型+烫发。

如果要使用八进制表示的权限,则需要:

perms=$(([##8] s[mode] & 8#7777))

BSD(包括Apple OS / X)也具有stat命令。

mode=$(stat -f %p some-file)
perm=$(printf %o "$((mode & 07777))"

GNU find(最早可以追溯到1990年甚至更早)可以将权限打印为八进制:

find some-file -prune -printf '%m\n'

后来(2001年,很久之后zsh stat(1997年),但在BSD stat(2002年)之前)stat,又引入命令,它具有不同的语法:

stat -c %a some-file

在此之前很久,IRIX已经有了一个stat命令(IRIX 5.3中已经有了该命令)使用另一种语法 1994年中):

stat -qp some-file

同样,当没有标准命令时,最好的可移植性是使用perl

perl -e 'printf "%o\n", (stat shift)[2]&07777' some-file

15

您可以stat使用该-c选项要求GNU 以八进制格式输出权限。来自man 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

因此,在您的情况下:

bash-4.2$ ls -l foo
-rw-r--r-- 1 manatwork manatwork 0 Apr  7 19:43 foo

bash-4.2$ stat -c '%a' foo
644

或者,您甚至可以通过将stat输出格式化为有效命令来使其自动化:

bash-4.2$ stat -c "chmod %a '%n'" foo
chmod 644 'foo'

bash-4.2$ stat -c "chmod %a '%n'" foo > setpermission.sh

bash-4.2$ chmod a= foo

bash-4.2$ ls -l foo
---------- 1 manatwork manatwork 0 Apr  7 19:43 foo

bash-4.2$ sh setpermission.sh 

bash-4.2$ ls -l foo
-rw-r--r-- 1 manatwork manatwork 0 Apr  7 19:43 foo

如果使用通配符,上述解决方案也适用于多个文件:

stat -c "chmod -- %a '%n'" -- *

可以与包含空格字符的文件名一起正常使用,但对包含单引号的文件名将失败。


2
stat没有-c选择。我正在使用OS X 10.8.3。
Tyilo

感谢您提供信息,@ Tyilo。很抱歉,我无法使用OS X的工具。
manatwork

尝试阅读Mac OS X上的manpage ^ W ^ W ^ W stat(1)具有-f标志,用于指定输出格式,例如stat -f 'chmod %p "%N"'
gelraen 2013年

11

为了从符号转换为八进制符号,我曾经想出过

chmod_format() {
  sed 's/.\(.........\).*/\1/
    h;y/rwsxtSTlL-/IIIIIOOOOO/;x;s/..\(.\)..\(.\)..\(.\)/|\1\2\3/
    y/sStTlLx-/IIIIIIOO/;G
    s/\n\(.*\)/\1;OOO0OOI1OIO2OII3IOO4IOI5IIO6III7/;:k
    s/|\(...\)\(.*;.*\1\(.\)\)/\3|\2/;tk
    s/^0*\(..*\)|.*/\1/;q'
}

展开:

#! /bin/sed -f
s/.\(.........\).*/\1/; # extract permissions and discard the rest

h; # store a copy on the hold space

# Now for the 3 lowest octal digits (rwx), translates the flags to
# binary where O means 0 and I means 1.
# l, L are for mandatory locking (a regular file that has 02000 on
# and not 010 on some systems like Linux). Some ls implementations
# like GNU ls confusingly use S there like for directories even though 
# it has nothing to do with setgid in that case. Some ls implementations 
# use L, some others l (against POSIX which requires an uppercase
# flag for extra flags when the execution bit is not set).
y/rwsxtSTlL-/IIIIIOOOOO/

x; # swap hold and pattern space, to do a second processing on those flags.

# now only consider the "xXlLsStT" bits:
s/..\(.\)..\(.\)..\(.\)/|\1\2\3/

y/sStTlLx-/IIIIIIOO/; # make up the 4th octal digit as binary like before

G; # append the hold space so we now have all 4 octal digits as binary

# remove the extra newline and append a translation table
s/\n\(.*\)/\1;OOO0OOI1OIO2OII3IOO4IOI5IIO6III7/

:k
  # translate the OOO -> 0 ... III -> 7 in a loop
  s/|\(...\)\(.*;.*\1\(.\)\)/\3|\2/
tk

# trim leading 0s and our translation table.
s/^0*\(..*\)|.*/\1/;q

ls -l一个文件的输出中返回八进制数。

$ echo 'drwSr-sr-T' | chmod_format
7654

我在输出上使用了此功能dpkg,将权限设置回“安装时”。感谢您回答字面问题,而不考虑哪个命令产生了权限字符串。
HiTechHiTouch

3

在Mac下的sh上的此命令

stat -f "%Lp %N" your_files

如果只需要数字权限,则仅使用%Lp。

例如:

stat -f "%Lp %N" ~/Desktop
700 Desktop

700是可以在chmod中使用的数字权限,而Desktop是文件名。


2

这是对问题Y的回答(忽略问题X),受OP的尝试启发:

#!/bin/bash
LC_COLLATE=C
while read ls_out
do
        extra=0
        perms=0
        for i in {1..9}
        do
                # Shift $perms to the left one bit, so we can always just add the LSB.
                let $((perms*=2))
                this_char=${ls_out:i:1}
                # If it's different from its upper case equivalent,
                # it's a lower case letter, so the bit is set.
                # Unless it's "l" (lower case L), which is special.
                if [ "$this_char" != "${this_char^}" ]  &&  [ "$this_char" != "l" ]
                then
                        let $((perms++))
                fi
                # If it's not "r", "w", "x", or "-", it indicates that
                # one of the high-order (S/s=4000, S/s/L/l=2000, or T/t=1000) bits
                # is set.
                case "$this_char" in
                  ([^rwx-])
                        let $((extra += 2 ** (3-i/3) ))
                esac
        done
        printf "%o%.3o\n" "$extra" "$perms"
done

上面包含了一些bashisms。以下版本似乎符合POSIX:

#!/bin/sh
LC_COLLATE=C
while read ls_out
do
        extra=0
        perms=0
        for i in $(seq 1 9)
        do
                # Shift $perms to the left one bit, so we can always just add the LSB.
                : $((perms*=2))
                this_char=$(expr "$ls_out" : ".\{$i\}\(.\)")
                # Lower case letters other than "l" indicate that permission bits are set.
                # If it's not "r", "w", "x", or "-", it indicates that
                case "$this_char" in
                  (l)
                        ;;
                  ([a-z])
                        : $((perms+=1))
                esac
                # If it's not "r", "w", "x", or "-", it indicates that
                # one of the high-order (S/s=4000, S/s/L/l=2000, or T/t=1000) bits
                # is set.
                case "$this_char" in
                  ([!rwx-])
                        : $((extra += 1 << (3-i/3) ))
                esac
        done
        printf "%o%.3o\n" "$extra" "$perms"
done

笔记:

  • LC_COLLATE=C告诉shell将字母序列范围的模式与使用ASCII顺序,因此[a-e]相当于[abcde]。在某些语言环境中(例如en_US),[a-e]它等效于[aAbBcCdDeE] (即[abcdeABCDE]),或者[abcdeABCD]-请参见为什么bash case语句不区分大小写...?
  • 在第二个版本(与POSIX兼容的版本)中:

    • 第一条case语句可以重写:

              case "$this_char" in
                ([a-km-z])
                      : $((perms+=1))
              esac
      

      但是我认为我现在所拥有的方式使人们更容易看出来l 这封信的处理方式有所不同。或者,可以将其重写:

              case "$this_char" in
                ([rwxst])
                      : $((perms+=1))
              esac
      

      因为rwxs,和t都应该永远出现在模式字符串(除了那唯一的字母l)。

    • 第二条case语句可以重写:

              case "$this_char" in
                ([rwx])
                      ;;
                ([A-Za-z])
                      : $((extra += 1 << (3-i/3) ))
               esac
      

      强制执行仅字母对指定模式位有效的规则。(相比之下,完整脚本中更简洁的版本是惰性的,将接受-rw@rw#rw%等效于  rwSrwSrwT。)或者,可以将其重写:

              case "$this_char" in
                ([SsTtLl])
                      : $((extra += 1 << (3-i/3) ))
              esac
      

      因为SsTtL,和l都应该永远出现在模式字符串,唯一的字母(除rwx)。

用法:

$ echo drwxr-xr-x | chmod-format
0755
$ echo -rwsr-sr-x | chmod-format
6755
$ echo -rwSr-Sr-- | chmod-format
6644
$ echo -rw-r-lr-- | chmod-format
2644
$ echo ---------- | chmod-format
0000

而且,是的,我知道最好不要使用echo可能以-; 开头的文本;我只是想复制问题中的用法示例。注意,显然,它忽略了第0个字符(即前导d/ b/ c/ -/ l/ p/ s/ / D)和第10个字符(+/ ./ @)。假设的维护者ls永远不会在第三个,第六个或第九个位置将r/ Rw/ 定义 W为有效字符(并且,如果确实如此,则应使用木棍将其打败)。


另外,我刚刚在如何恢复/ var下所有文件的默认组/用户所有权下 通过cas找到了以下代码:

        let perms=0

        [[ "${string}" = ?r???????? ]]  &&  perms=$(( perms +  400 ))
        [[ "${string}" = ??w??????? ]]  &&  perms=$(( perms +  200 ))
        [[ "${string}" = ???x?????? ]]  &&  perms=$(( perms +  100 ))
        [[ "${string}" = ???s?????? ]]  &&  perms=$(( perms + 4100 ))
        [[ "${string}" = ???S?????? ]]  &&  perms=$(( perms + 4000 ))
        [[ "${string}" = ????r????? ]]  &&  perms=$(( perms +   40 ))
        [[ "${string}" = ?????w???? ]]  &&  perms=$(( perms +   20 ))
        [[ "${string}" = ??????x??? ]]  &&  perms=$(( perms +   10 ))
        [[ "${string}" = ??????s??? ]]  &&  perms=$(( perms + 2010 ))
        [[ "${string}" = ??????S??? ]]  &&  perms=$(( perms + 2000 ))
        [[ "${string}" = ???????r?? ]]  &&  perms=$(( perms +    4 ))
        [[ "${string}" = ????????w? ]]  &&  perms=$(( perms +    2 ))
        [[ "${string}" = ?????????x ]]  &&  perms=$(( perms +    1 ))
        [[ "${string}" = ?????????t ]]  &&  perms=$(( perms + 1001 ))
        [[ "${string}" = ?????????T ]]  &&  perms=$(( perms + 1000 ))

我已经测试了此代码(但没有彻底地测试过),它似乎可以工作,除了它不能识别l或排L在第六位。但是请注意,尽管此答案在简洁性和清晰度方面是优越的,但我的却实际上更短(仅计算循环的代码;处理单个-rwxrwxrwx字符串的代码,不计算注释),并且它甚至可以变得更短通过更换 用。if condition; then …condition && …


当然,您不应该解析的输出ls


@StéphaneChazelas:好的,我说了#!/bin/sh,然后使用了一些bashisms。哎呀。但是你错过了两个:和似乎并不符合POSIX是(标准没有提及在所有,是疯狂的对和)。相反,没有使用;这仅出现在cas的答案中,我在这里引用了它。另外,我的答案确实可以处理“ l”和“ L”,并且我已经指出了cas的答案不正确的事实。$(( variable++ ))$(( number ** number ))**++-- [[…]]
斯科特

对不起,L / L [[,我读得太快了。是的--和++不是POSIX。POSIX允许外壳程序实现它们,这意味着$((- -a))如果要双重否定则必须编写,而不是$((--a))用来表示减量操作。
斯特凡Chazelas

请注意,这seq不是POSIX命令。您可以使用$ {var#?}运算符来避免expr。并不是说LC_COLLATE不会覆盖LC_ALL
StéphaneChazelas 2015年

@StéphaneChazelas:好的,您现在又在谈论cas的答案,对吗?他采用了“懒惰”的方法,即使用十进制算术来构造一个看起来像八进制数字的字符串。请注意,他的所有步长值(4000、2000、1000、400、200、100、40、20和10)都是十进制数。但是,由于没有8'或9',也没有办法获得超过7小数点后的位数,因此他可以伪装。………………(此评论是对消失的StéphaneChazelas评论的回应。)
Scott

是的,我后来才意识到,这就是为什么我删除评论。
斯特凡Chazelas


0

另一种选择是,如果要保存权限,以便以后再将其还原或保存到其他文件上,则可以使用setfacl/getfacl,它也可以还原(POSIX-草稿)ACL作为奖励。

getfacl some-file > saved-perms
setfacl -M saved-perms some-other-file

(在Solaris上,使用-f代替-M)。

但是,尽管它们在某些BSD上可用,但在仅使用ACL进行操作的Apple OS / X上却没有chmod


0

在Mac OS X(10.6.8)上,您必须使用stat -f format(因为它实际上是NetBSD / FreeBSD stat)。

# using Bash

mods="$(stat -f "%p" ~)"    # octal notation
mods="${mods: -4}"
echo "$mods"

mods="$(stat -f "%Sp" ~)"  # symbolic notation
mods="${mods: -9}"
echo "$mods"

要将仅产生的符号权限字符串ls -l转换为八进制(仅使用shell内置),请参见:showperm.bash

# from: showperm.bash
# usage: showperm modestring
#
# example: showperm '-rwsr-x--x'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.