Конвертировать формат вывода 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.

Tyilo
источник
2
Намного проще с stat. У тебя есть это? (Это инструмент GNU, так что в основном он доступен в Linux, а не в Unix.)
manatwork
@manatwork stat fooдает 16777219 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

Ответы:

24

В некоторых системах есть команды для отображения прав доступа к файлу в виде числа, но, к сожалению, ничего переносимого.

zshимеет stat(иначе zstat) встроенный в statмодуль:

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была введена команда GNU с другим синтаксисом:

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

Вы можете попросить GNU statвывести разрешения в восьмеричном формате, используя -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'" -- *

Будет корректно работать с именами файлов, содержащими пробельные символы, но не будет работать с именами файлов, содержащими одинарные кавычки.

manatwork
источник
2
У меня statнет -cвыбора. Я использую OS X 10.8.3.
Tyilo
Спасибо за информацию, @Tyilo. И извините, я не могу помочь с инструментами OS X.
manatwork
Попробуйте прочитать man-страницу ^ W ^ W ^ W stat (1) в Mac OS X с флагом -f для указания формата вывода, напримерstat -f 'chmod %p "%N"'
gelraen
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'
}

Expanded:

#! /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 - это имя файла.

TonyL2ca
источник
2

Вот ответ на вопрос Y (игнорируя вопрос X ), вдохновленный попыткой ОП:

#!/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велит оболочке обрабатывать шаблоны диапазонов буквенных последовательностей, используя порядок ASCII, что [a-e]эквивалентно [abcde]. В некоторых локалях (например, en_US) [a-e]эквивалентно [aAbBcCdDeE] (т. Е. [abcdeABCDE]) Или, возможно, [abcdeABCD]- см. Почему оператор bash не учитывает регистр…? )
  • Во второй версии (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/ s/ D) и 10-й ( +/ ./ @). Предполагается, что сопровождающие 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 .

Скотт
источник
@ StéphaneChazelas: Хорошо, я сказал, #!/bin/shа затем использовал несколько шуток. К сожалению. Но вы пропустили пару: и , похоже, тоже не POSIX (Стандарт вообще не упоминается , а коротко и ). Наоборот, я не использовал ; который появляется только в Cas ответ «ы, которые я цитировал из здесь . Кроме того, мой ответ обрабатывает 'l' и 'L', и я уже указал на тот факт, что ответ cas не делает. $(( variable++ ))$(( number ** number ))**++-- [[…]]
Скотт
Извините за л / л [[, я читаю слишком быстро. Да, - и ++ не POSIX. POSIX позволяет оболочкам реализовывать их, что означает, что вы должны писать, $((- -a))если хотите двойное отрицание, а не то, что вы можете использовать $((--a))для обозначения операции декремента.
Стефан Шазелас
Обратите внимание, что seqэто не команда POSIX. Вы можете использовать оператор $ {var #?}, Чтобы избежать выражения expr. Не то чтобы LC_COLLATE не перекрывал LC_ALL
Стефан
@ StéphaneChazelas: Хорошо, вы снова говорите об ответе cas, верно? Он использует «ленивый» подход использования десятичной арифметики для построения строки, которая выглядит как восьмеричное число. Обратите внимание, что все значения его шагов (4000, 2000, 1000, 400, 200, 100, 40, 20 и 10) являются десятичными числами. Но, поскольку нет ни 8«s», ни 9«s», и нет способа получить больше, чем 7в любой десятичной позиции, он может вырваться из шарады. ... ... ... ... ... ... ... ... (Этот комментарий является ответом на комментарий Stéphane Chazelas , который исчез.)
Скотт
Да, я понял это позже, поэтому я удалил комментарий.
Стефан Шазелас
1

Если ваша цель состоит в том, чтобы взять разрешения из одного файла и передать их другому, в GNU для этого chmodуже есть «справочная» опция .

Bratchley
источник
ОП упоминал, что он был в Apple OS / X, поэтому chmodGNU chmodтам не будет .
Стефан Шазелас
Ах да, я вижу комментарий к другому ответу, где они говорят свою платформу. Мой не единственный, упоминающий GNU, хотя, и вы можете получить GNU Utilities на Mac OS X
Bratchley
0

В качестве альтернативы, если вы хотите сохранить разрешения, восстановить их позже или использовать другой файл, это также использование setfacl/getfaclсписков ACL (черновик POSIX) в качестве бонуса.

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

(на Солярисе используйте -fвместо -M).

Однако, хотя они доступны на некоторых BSD, они не на Apple OS / X, где манипулируют chmodтолько ACL .

Стефан Шазелас
источник
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 в восьмеричное значение (используя только встроенные функции оболочки), см. Showperm.bash .

# from: showperm.bash
# usage: showperm modestring
#
# example: showperm '-rwsr-x--x'
phil32
источник