假设我有以下输出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。
答案1
有些系统有命令将文件的权限显示为数字,但不幸的是,没有任何可移植的。
zsh
模块中有一个stat
(又名)内置函数:zstat
stat
zmodload zsh/stat
stat -H s some-file
那么,mode
is中的$s[mode]
but是模式,即type + perms。
如果您想要以八进制表示的权限,您需要:
perms=$(([##8] s[mode] & 8#7777))
BSD(包括苹果操作系统/Xstat
)也有一个命令。
stat -f %Lp some-file
(没有L
,完整的模式返回,以八进制表示)
GNU find(最早可以追溯到 1990 年,可能更早)可以将权限打印为八进制:
find some-file -prune -printf '%m\n'
后来(2001 年,在zsh
stat
(1997 年)很久之后,但在 BSD stat
(2002 年)之前),GNUstat
命令再次引入了不同的语法:
stat -c %a some-file
早在这些之前,IRIX 就已经有了一个stat
命令(已经存在于IRIX 5.31994 年),使用另一种语法:
stat -qp some-file
同样,当没有标准命令时,可移植性的最佳选择是使用perl
:
perl -e 'printf "%o\n", (stat shift)[2]&07777' some-file
答案2
您可以stat
使用该选项要求 GNU 以八进制格式输出权限-c
。从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'" -- *
对于包含空格字符的文件名可以正常工作,但对于包含单引号的文件名将失败。
答案3
为了从符号转换为八进制表示法,我一旦出现和:
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
答案4
这是问题的答案是(忽略问题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
以上内容包含一些攻击性内容。以下版本似乎符合 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]
(ie,[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
因为
r
、w
、x
、s
和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
因为
S
,s
,T
,t
,L
, 和l
是唯一应该出现在模式字符串中的字母(r
,w
, 和除外x
)。
用法:
$ 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
/ )和第 10 个字符 ( / / )。它假设 的维护者永远不会将 /或/定义为第三、第六或第九位置的有效字符(并且,如果他们这样做,他们应该是s
D
+
.
@
ls
r
R
w
W
用棍棒殴打)。
另外,我刚刚找到了以下代码,通过CAS, 在下面 如何恢复 /var 下所有文件的默认组/用户所有权:
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
。