Konwertuj format wyjściowy ls -l na format chmod

17

Powiedz, że mam następujące dane wyjściowe z ls -l:

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

Jak mogę automatycznie przekonwertować to na format używany przez chmod?

Na przykład:

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

Używam OS X 10.8.3.

Tyilo
źródło
2
O wiele łatwiej dzięki stat. Masz to? (Jest to narzędzie GNU, więc jest dostępne głównie w Linuksie, a nie w Uniksie).
manatwork
@manatwork stat foodaje 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. Nie widzę 755w tym
Tyilo,

Odpowiedzi:

24

Niektóre systemy mają polecenia do wyświetlania uprawnień do pliku jako liczby, ale niestety nic przenośnego.

zshma stat(aka zstat) wbudowany w statmoduł:

zmodload zsh/stat
stat -H s some-file

Następnie modejest $s[mode]jednak to tryb, który jest typu + perms.

Jeśli chcesz uprawnienia wyrażone ósemkowo, potrzebujesz:

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

BSD (w tym Apple OS / X ) również mają statpolecenie.

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

GNU find (już od 1990 roku i prawdopodobnie wcześniej) może wydrukować uprawnienia w postaci ósemkowej:

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

Później (2001, długo po zsh stat(1997), ale przed BSD stat(2002)) wprowadzono statkomendę GNU z inną składnią:

stat -c %a some-file

Na długo przed nimi IRIX miał już statkomendę (już w IRIX 5.3 w 1994) z inną składnią:

stat -qp some-file

Ponownie, gdy nie ma standardowego polecenia, najlepszym wyborem dla przenośności jest użycie perl:

perl -e 'printf "%o\n", (stat shift)[2]&07777' some-file
Stéphane Chazelas
źródło
15

Możesz poprosić GNU stato wyświetlanie uprawnień w formacie ósemkowym, korzystając z -copcji. Od 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

Więc w twoim przypadku:

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

Lub możesz to zautomatyzować, formatując statdane wyjściowe jako prawidłowe polecenie:

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

Powyższe rozwiązanie będzie działać również w przypadku wielu plików, jeśli używasz znaku wieloznacznego:

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

Działa poprawnie z nazwami plików zawierającymi znaki spacji, ale zawiedzie w przypadku nazw plików zawierających pojedyncze cudzysłowy.

człowiek w pracy
źródło
2
Mój statnie ma -copcji. Używam OS X 10.8.3.
Tyilo,
Dzięki za informację, @Tyilo. I przepraszam, nie mogę pomóc z narzędziami OS X.
manatwork
Spróbuj przeczytać manpage ^ W ^ W ^ W stat (1) na Mac OS X mieć flagę -f do określenia formatu wyjściowego, np.stat -f 'chmod %p "%N"'
gelraen
11

Aby przekonwertować notację symboliczną na ósemkową, kiedyś wpadłem na pomysł :

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'
}

Rozszerzony:

#! /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

Zwraca liczbę ósemkową z wyniku ls -ljednego pliku.

$ echo 'drwSr-sr-T' | chmod_format
7654
Stéphane Chazelas
źródło
Użyłem tego na wyjściu, dpkgaby ustawić uprawnienia z powrotem na „jak zainstalowane”. Dziękujemy za odpowiedź na dosłowne pytanie, bez względu na to, które polecenie wygenerowało ciąg uprawnień.
HiTechHiTouch
3

To polecenie na Macu pod sh

stat -f "%Lp %N" your_files

jeśli chcesz tylko uprawnienia numerycznego, użyj tylko% Lp.

na przykład:

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

700 to uprawnienie numeryczne, którego można używać w chmod, a Desktop to nazwa pliku.

TonyL2ca
źródło
2

Oto odpowiedź na pytanie Y (ignorując pytanie X ), zainspirowana próbą 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

Powyższe zawiera kilka baszizmów. Następująca wersja wydaje się być zgodna z 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

Uwagi:

  • LC_COLLATE=CMówi powłoce na pismo traktować jako sekwencja zakres wzorców korzystania kolejność ASCII, więc [a-e]jest równoważna [abcde]. W niektórych lokalizacjach (np. En_US) [a-e]jest równoważne [aAbBcCdDeE] (tj. [abcdeABCDE]), A może [abcdeABCD]- zobacz Dlaczego w instrukcji bash wielkość liter nie jest rozróżniana…? )
  • W drugiej wersji (zgodnej z POSIX):

    • Pierwsze casezdanie można przepisać:

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

      ale myślę, że sposób, w jaki go mam, ułatwia teraz dostrzeżenie, że l list jest traktowany inaczej. Alternatywnie można go przepisać:

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

      ponieważ r, w, x, s, i tsą tylko litery, które powinny zawsze pojawiają się w ciąg trybie (innym niż l).

    • Drugie casezdanie można przepisać:

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

      aby wymusić regułę, że tylko litery są poprawne przy określaniu bitów trybu. (Natomiast bardziej zwięzła wersja pełnego skryptu jest leniwa i zaakceptuje -rw@rw#rw%jako równoważną  rwSrwSrwT.) Alternatywnie można ją przepisać:

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

      ponieważ S, s, T, t, L, i lsą tylko litery, które powinny zawsze pojawiają się w ciąg tryb (inne niż r, wi x).

Stosowanie:

$ 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

I tak, wiem, że lepiej nie używać echotekstu, który może zaczynać się od -; Chciałem po prostu skopiować przykład użycia z pytania. Zauważ oczywiście, że ignoruje to znak 0 (tj. Wiodący d/ b/ c/ -/ l/ p/ s/ D) i 10. ( +/ ./ @). Zakłada się, że opiekunowie lsnigdy nie zdefiniują r/ Rlub w/ Wjako prawidłowych znaków na trzeciej, szóstej lub dziewiątej pozycji (a jeśli tak, powinni zostać pobici kijami ).


Właśnie znalazłem następujący kod, według cas , w części Jak przywrócić domyślną własność grupy / użytkownika dla wszystkich plików w katalogu / 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 ))

Przetestowałem ten kod (ale nie do końca) i wydaje się, że działa, poza tym, że nie rozpoznaje lani nie zajmuje Lszóstej pozycji. Zauważ jednak, że chociaż ta odpowiedź jest lepsza pod względem prostoty i przejrzystości, moja jest w rzeczywistości krótsza (licząc tylko kod w pętli; kod, który obsługuje pojedynczy -rwxrwxrwxciąg, nie licząc komentarzy), i może być jeszcze krótsza zastępując z .if condition; then …condition && …


Oczywiście nie powinieneś analizować wynikuls .

Scott
źródło
@ StéphaneChazelas: OK, powiedziałem, #!/bin/sha potem użyłem kilku bachizmów. Ups Ale przegapiłeś parę: i nie wydajesz się być POSIX (Standard w ogóle nie wspomina , i jest wiewiórki i ). Z drugiej strony, ja nie używać ; który pojawia się tylko w cas odpowiedź „s, które cytowany z tutaj . Również moja odpowiedź radzi sobie „L” i „L”, a ja już zwrócił uwagę na fakt, że odpowiedź TAS nie. $(( variable++ ))$(( number ** number ))**++-- [[…]]
Scott,
Przepraszam za l / L [[, czytam za szybko. Tak, - i ++ nie są POSIX. POSIX pozwala powłokom na ich implementację, co oznacza, że ​​musisz pisać, $((- -a))jeśli chcesz podwójnej negacji, a nie, że możesz użyć tego, $((--a))by oznaczać operację zmniejszania.
Stéphane Chazelas,
Zauważ, że seqto nie jest polecenie POSIX. Możesz być w stanie użyć operatora $ {var #?}, Aby uniknąć wyrażenia. Nie to, że LC_COLLATE nie zastąpi LC_ALL
Stéphane Chazelas
@ StéphaneChazelas: OK, mówisz teraz o odpowiedzi cas, prawda? Stosuje „leniwe” podejście polegające na wykorzystaniu arytmetyki dziesiętnej do skonstruowania ciągu, który wygląda jak liczba ósemkowa. Zauważ, że wszystkie jego wartości kroku (4000, 2000, 1000, 400, 200, 100, 40, 20 i 10) są liczbami dziesiętnymi. Ale ponieważ nie istnieją 8„s lub 9” s, a nie sposób, aby uzyskać więcej niż 7w dowolnej pozycji dziesiętnych, może wyciągnąć z farsy. …………………… (Ten komentarz jest odpowiedzią na komentarz Stéphane Chazelas, który zniknął.)
Scott
Tak, zdałem sobie z tego sprawę później i dlatego usunąłem komentarz.
Stéphane Chazelas,
1

Jeśli twoim celem jest pobranie uprawnień z jednego pliku i przekazanie ich także do innego, GNU chmodma już opcję „referencyjną” .

Bratchley
źródło
OP wspomniał, że był na Apple OS / X, więc chmodnie będzie chmodtam GNU .
Stéphane Chazelas,
Ach tak, widzę komentarz do drugiej odpowiedzi, w której mówią o swojej platformie. Mój nie jest jedynym, który wspomina o GNU, a GNU Utilities można uzyskać na Mac OS X
Bratchley,
0

Alternatywą jest użycie opcji, jeśli chcesz zapisać uprawnienia w celu ich późniejszego przywrócenia lub w innym pliku setfacl/getfacl, a także przywrócić listy ACL (POSIX-draft) jako bonus.

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

(w systemie Solaris użyj -fzamiast -M).

Jednak chociaż są one dostępne w niektórych BSD, nie są one w systemie Apple OS / X, w którym manipulowane są chmodtylko listy ACL .

Stéphane Chazelas
źródło
0

W systemie Mac OS X (10.6.8) musisz użyć stat -f format(ponieważ w rzeczywistości jest to 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"

Aby po prostu przetłumaczyć symboliczny ciąg uprawnień wytworzony przez ls -lna ósemkę (używając tylko wbudowanych powłok) zobacz: showperm.bash .

# from: showperm.bash
# usage: showperm modestring
#
# example: showperm '-rwsr-x--x'
phil32
źródło