Перетворити вихідний формат 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 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в цьому.
Тиїло

Відповіді:


24

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

zshмає в модулі stat(aka zstat) вбудований stat:

zmodload zsh/stat
stat -H s some-file

Потім, режим modeє, $s[mode]але це режим, тобто тип + perms.

Якщо ви хочете, щоб дозволи, виражені у восьмериці, вам потрібно:

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

BSD (включаючи Apple OS / X ) також мають statкоманду.

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

Знайдіть GNU (починаючи з 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'" -- *

Працюватиме правильно з іменами файлів, що містять символи пробілу, але не вдасться назвати файли, що містять одиничні лапки.


2
У мене statнемає -cможливості. Я використовую OS X 10.8.3.
Тиїло

Дякую за інформацію, @Tyilo. І вибачте, я не можу допомогти з інструментами OS X.
манатство

Спробуйте прочитати manpage ^ W ^ W ^ W stat (1) на Mac OS X, stat -f 'chmod %p "%N"'
встановіть

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 під ш

stat -f "%Lp %N" your_files

якщо ви хочете лише числовий дозвіл, використовуйте лише% Lp.

наприклад:

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

700 - числовий дозвіл, який можна використовувати в chmod, а Desktop - ім'я файлу.


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 не
переможе

@ StéphaneChazelas: Гаразд, зараз ти знову говориш про відповідь Cas, правда? Він використовує "ледачий" підхід використання десяткової арифметики для побудови рядка, схожого на вісімкове число. Зауважте, що всі його крокові значення (4000, 2000, 1000, 400, 200, 100, 40, 20 та 10) є десятковими числами. Але, оскільки у нас немає 8«з або 9» s, і немає можливості отримати більше , ніж 7в будь-який десяткової позиції, він може потягнути шаради. …………………… (Цей коментар є відповіддю на коментар Стефана Шазеласа, який зник.)
Скотт

Так, я зрозумів це пізніше, саме тому я видалив коментар.
Стефан Шазелас

1

Якщо ваша мета - взяти дозволи з одного файлу і надати їх іншому, GNU chmodвже має "посилання" на це .


ОП згадував, що він був в Apple OS / X, тому chmodGNU chmodтам не буде .
Стефан Шазелас

Ну так, я бачу коментар до іншої відповіді, де вони говорять про свою платформу. Шахта не є єдиною згадкою про GNU, і ви можете отримати утиліти GNU на Mac OS X
Bratchley

0

Якщо ви хочете зберегти дозволи, відновлювати їх згодом, або в іншому файлі, це використовувати setfacl/getfacl, і він також відновить (POSIX-чернетку) ACL як бонус.

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

(на Solaris, використовувати -fзамість -M).

Однак, хоча вони доступні на деяких BSD, вони не є в Apple OS / X, де ACL маніпулюють 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у восьмеричний (використовуючи лише вбудовані оболонки), див.: Showperm.bash .

# from: showperm.bash
# usage: showperm modestring
#
# example: showperm '-rwsr-x--x'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.