Converta o formato de saída ls -l para o formato chmod

17

Digamos que tenho a seguinte saída de ls -l:

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

Como posso converter isso automaticamente para o formato usado por chmod?

Por exemplo:

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

Estou usando o OS X 10.8.3.

Tyilo
fonte
2
Muito mais fácil com stat. Você tem? (É uma ferramenta GNU, por isso, na maior parte disponíveis no Linux, e não em Unix.)
manatwork
@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. Eu não vejo 755nele.
Tyilo

Respostas:

24

Alguns sistemas possuem comandos para exibir as permissões de um arquivo como um número, mas, infelizmente, nada portátil.

zshpossui um stat(aka zstat) embutido no statmódulo:

zmodload zsh/stat
stat -H s some-file

Em seguida, o modeestá em $s[mode]mas é o modo, que é do tipo + perms.

Se você deseja que as permissões sejam expressas em octal, é necessário:

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

Os BSDs (incluindo Apple OS / X ) também têm um statcomando.

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

O GNU find (desde 1990 e provavelmente antes) pode imprimir as permissões como octal:

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

Mais tarde (2001, muito depois zsh stat(1997), mas antes do BSD stat(2002)), um statcomando GNU foi introduzido com outra sintaxe diferente:

stat -c %a some-file

Muito antes disso, o IRIX já possuía um statcomando (já existente no IRIX 5.3 em 1994) com outra sintaxe:

stat -qp some-file

Novamente, quando não há comando padrão, a melhor aposta para portabilidade é usar perl:

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

Você pode pedir statao GNU para emitir as permissões no formato octal usando a -copção De 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

Então, no seu caso:

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

Ou você pode até automatizá-lo formatando stata saída como comando válido:

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

A solução acima também funcionará para vários arquivos se você estiver usando um curinga:

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

Funcionará corretamente com nomes de arquivos contendo caracteres de espaço em branco, mas falhará em nomes de arquivos que contenham aspas simples.

homem a trabalhar
fonte
2
Meu statnão tem uma -copção. Estou usando o OS X 10.8.3.
Tyilo
Obrigado pela informação, @Tyilo. E desculpe, não posso ajudar com as ferramentas do OS X.
manatwork
Tente ler manpage ^ W ^ W ^ W stat (1) no Mac OS X têm sinalizador -f para especificar o formato de saída, por exemplostat -f 'chmod %p "%N"'
gelraen
11

Para converter da notação simbólica para octal, uma vez me deparei com:

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

Expandido:

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

Isso retorna o número octal da saída de ls -lum arquivo.

$ echo 'drwSr-sr-T' | chmod_format
7654
Stéphane Chazelas
fonte
Eu usei isso na saída de dpkgpara definir permissões de volta para "como instalado". Obrigado por responder à pergunta literal sem levar em consideração qual comando produziu a sequência de permissões.
HiTechHiTouch
3

Este comando no Mac sob sh

stat -f "%Lp %N" your_files

se você deseja apenas a permissão numérica, use apenas% Lp.

por exemplo:

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

O 700 é a permissão numérica que pode ser usada no chmod e Desktop é o nome do arquivo.

TonyL2ca
fonte
2

Aqui está uma resposta para a pergunta Y (ignorando a pergunta X ), inspirada na tentativa do 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

O texto acima contém alguns basismos. A seguinte versão parece ser compatível com 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

Notas:

  • O LC_COLLATE=Ccomando diz ao shell para tratar os padrões de intervalo de sequência de letras como usando a ordem ASCII, portanto [a-e]é equivalente a [abcde]. Em alguns locais (por exemplo, en_US), [a-e]é equivalente a [aAbBcCdDeE] (ou seja, [abcdeABCDE]) ou talvez [abcdeABCD]- consulte Por que a instrução bash case não diferencia maiúsculas de minúsculas…? )
  • Na segunda versão (a compatível com POSIX):

    • A primeira casedeclaração pode ser reescrita:

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

      mas acho que do jeito que tenho agora facilita ver que l é a letra que está sendo tratada de maneira diferente. Como alternativa, ele pode ser reescrito:

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

      desde r, w, x, s, e tsão as únicas cartas que nunca deve aparecer em uma string de modo (diferente l).

    • A segunda casedeclaração pode ser reescrita:

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

      para impor a regra de que apenas letras são válidas para especificar bits de modo. (Por outro lado, a versão mais sucinta do script completo é preguiçosa e aceita -rw@rw#rw%como equivalente a  rwSrwSrwT.) Como alternativa, ela pode ser reescrita:

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

      desde S, s, T, t, L, e lsão as únicas cartas que nunca deve aparecer em uma string modo (exceto r, w, e x).

Uso:

$ 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

E, sim, eu sei que é melhor não usar echocom texto que possa começar -; Eu só queria copiar o exemplo de uso da pergunta. Observe, obviamente, que isso ignora o 0º caractere (ou seja, o d/ b/ c/ -/ l/ p/ s/ D) inicial e o 10º ( +/ ./ @). Ele pressupõe que os mantenedores de lsnunca definirão r/ Rou w/ Wcomo caracteres válidos na terceira, sexta ou nona posição (e, se o fizerem, devem ser espancados com paus ).


Além disso, encontrei o código a seguir, por cas , em Como restaurar a propriedade padrão do grupo / usuário de todos os arquivos em / 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 ))

Eu testei esse código (mas não completamente) e parece funcionar, exceto pelo fato de que ele não reconhece lou está Lna sexta posição. Observe, porém, que, embora essa resposta seja superior em termos de simplicidade e clareza, a minha é realmente mais curta (contando apenas o código dentro do loop; o código que lida com uma única -rwxrwxrwxsequência, sem contar comentários), e pode ser ainda mais curta substituindo por .if condition; then …condition && …


Obviamente, você não deve analisar a saída dels .

Scott
fonte
@ StéphaneChazelas: OK, eu disse #!/bin/she depois usei alguns basismos. Opa Mas você perdeu alguns:$(( variable++ )) e não parecem ser POSIX quer (o padrão não menciona em tudo, e é squirrelly em e ). Por outro lado, eu não usei ; que aparece apenas em cas resposta ‘s, que citou a partir de aqui . Além disso, minha resposta lida com 'l' e 'L', e eu já apontei o fato de que a resposta de cas não. $(( number ** number ))**++-- [[…]]
23715 Scott Scott
Desculpe por l / L [[, eu li muito rápido. Sim, - e ++ não são POSIX. O POSIX permite que os shells os implementem, o que significa que você deve escrever $((- -a))se quiser uma dupla negação, não que você possa usar $((--a))para significar uma operação de decremento.
Stéphane Chazelas
Observe que seqnão é um comando POSIX. Você pode usar o operador $ {var #?} Para evitar expr. Não que LC_COLLATE não substitua LC_ALL
Stéphane Chazelas
@ StéphaneChazelas: OK, você está falando sobre a resposta de cas agora, certo? Ele está adotando a abordagem "preguiçosa" do uso da aritmética decimal para construir uma string que se parece com um número octal. Observe que todos os seus valores de passo (4000, 2000, 1000, 400, 200, 100, 40, 20 e 10) são números decimais. Mas, como não há 8ou 9não, e não há maneira de obter mais do que 7em qualquer posição decimal, ele pode retirar a farsa. ... ... ... ... ... ... ... ... (Este comentário é uma resposta a um comentário Stéphane Chazelas que desapareceu.)
Scott
Sim, percebi isso mais tarde, e foi por isso que excluí o comentário.
Stéphane Chazelas
1

Se seu objetivo é obter permissões de um arquivo e também concedê-las a outro, o GNU chmodjá possui uma opção de "referência" para isso .

Bratchley
fonte
O OP mencionou que ele estava no Apple OS / X, então chmodnão será o GNU chmodlá.
Stéphane Chazelas
Ah, sim, estou vendo o comentário na outra resposta, onde eles dizem sua plataforma. Mina não é o único mencionar GNU embora e você pode obter GNU Utilitários no Mac OS X
Bratchley
0

Uma alternativa, se você quiser salvar as permissões, restaurá-las posteriormente ou em um arquivo diferente, é usar setfacl/getfacle também restaurará ACLs (rascunho do POSIX) como um bônus.

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

(no Solaris, use em -fvez de -M).

No entanto, embora estejam disponíveis em alguns BSDs, eles não estão no Apple OS / X onde as ACLs são manipuladas chmodapenas.

Stéphane Chazelas
fonte
0

No Mac OS X (10.6.8) você precisa usar stat -f format(porque na verdade é o 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"

Para traduzir apenas uma sequência de permissão simbólica produzida por ls -lem octal (usando apenas shellins internos), consulte: showperm.bash .

# from: showperm.bash
# usage: showperm modestring
#
# example: showperm '-rwsr-x--x'
phil32
fonte