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

12

Digamos que eu tenha 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.

    
por Tyilo 07.04.2013 / 17:51

8 respostas

22

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

zsh tem stat (também conhecido como zstat ) incorporado no módulo stat :

zmodload zsh/stat
stat -H s some-file

Em seguida, o mode está em $s[mode] , mas é o modo, ou seja, type + perms.

Se você quiser as permissões expressas em octal, será necessário:

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

BSDs (incluindo Apple OS / X ) tem um comando stat também.

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 de zsh stat (1997) mas antes de BSD stat (2002)) um comando GNU stat foi introduzido novamente com uma sintaxe diferente:

stat -c %a some-file

Muito antes disso, o IRIX já tinha um comando stat (já existe em IRIX 5.3 em 1994) com outra sintaxe:

stat -qp some-file

Mais uma vez, 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
    
por 07.04.2013 / 20:07
13

Você pode pedir ao GNU stat para exibir as permissões no formato octal usando a opção -c . 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 a saída de stat como um 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 usar um caractere 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.

    
por 07.04.2013 / 18:46
8

Para converter da notação simbólica para a octal, eu uma vez surgiu com:

chmod_format() {
  sed 's/.\(.........\).*//
    h;y/rwsxtSTlL-/IIIIIOOOOO/;x;s/..\(.\)..\(.\)..\(.\)/|/
    y/sStTlLx-/IIIIIIOO/;G
    s/\n\(.*\)/;OOO0OOI1OIO2OII3IOO4IOI5IIO6III7/;:k
    s/|\(...\)\(.*;.*\(.\)\)/|/;tk
    s/^0*\(..*\)|.*//;q'
}

Expandido:

#! /bin/sed -f
s/.\(.........\).*//; # 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/..\(.\)..\(.\)..\(.\)/|/

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\(.*\)/;OOO0OOI1OIO2OII3IOO4IOI5IIO6III7/

:k
  # translate the OOO -> 0 ... III -> 7 in a loop
  s/|\(...\)\(.*;.*\(.\)\)/|/
tk

# trim leading 0s and our translation table.
s/^0*\(..*\)|.*//;q

Isso retorna o número octal da saída de ls -l em um arquivo.

$ echo 'drwSr-sr-T' | chmod_format
7654
    
por 07.04.2013 / 18:57
3

Este comando no Mac sob sh

stat -f "%Lp %N" your_files

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

por exemplo:

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

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

    
por 03.03.2015 / 23:57
2

Aqui está uma resposta para a pergunta Y (ignorando pergunta X ), inspirado pela 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 acima contém alguns bashisms. 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=C diz ao shell para tratar padrões de intervalo de sequência de letras como usando a ordem ASCII, então [a-e] é equivalente a [abcde] . Em algumas localidades (por exemplo, en_US), [a-e] é equivalente a [aAbBcCdDeE] (por exemplo, [abcdeABCDE] ) ou talvez [abcdeABCD] - consulte Por que a instrução de caso não é sensível a maiúsculas e minúsculas…? )
  • Na segunda versão (a compatível com POSIX):

    • A primeira instrução case pode ser reescrita:

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

      mas acho que a maneira que eu tenho agora torna mais fácil ver que l é a letra que está sendo tratada de forma diferente. Alternativamente, pode ser reescrito:

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

      desde que r , w , x , s e t sejam as únicas letras que deve aparecer em uma string de modo (diferente de l ).

    • A segunda declaração case pode ser reescrita:

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

      para reforçar a regra de que apenas cartas são válidas para especificar bits de modo. (Em contraste, a versão mais sucinta do roteiro completo é preguiçosa, e aceitará -rw@rw#rw% como equivalente a rwSrwSrwT .) Alternativamente, pode ser reescrito:

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

      desde que S , s , T , t , L e l sejam as únicas letras que deve aparecer em uma string de modo (diferente de 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 echo com texto que possa começar com - ; Eu só queria copiar o exemplo de uso da pergunta. Note, obviamente, que isso ignora o 0º caractere (ou seja, o líder d / b / c / - / l / p / s / D ) e o décimo ( + / . / @ ). Ele assume que os mantenedores de ls nunca irão definir r / R ou w / W como caracteres válidos na terceira, sexta ou nona posição (e, se o fizerem, devem ser espancado com paus ).

Além disso, acabei de encontrar o seguinte código, 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 este código (mas não completamente), e parece funcionar, exceto pelo fato de não reconhecer l ou L na sexta posição. Note, no entanto, que enquanto esta resposta é superior em termos de simplicidade e clareza, o meu é realmente mais curto (contando apenas o código dentro do loop); o código que manipula uma única string -rwxrwxrwx , sem contar os comentários), e poderia ser ainda mais curto substituindo if condition; then … com condition && … .

É claro que você não deve analisar a saída de ls .

    
por 26.06.2015 / 08:41
1

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

    
por 07.04.2013 / 23:53
1

No Mac OS X (10.6.8) você tem que 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 cadeia de permissão simbólica produzida por ls -l em octal (usando somente shell builtins), consulte: showperm.bash .

# from: showperm.bash
# usage: showperm modestring
#
# example: showperm '-rwsr-x--x'
    
por 08.04.2013 / 15:49
0

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

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

(no Solaris, use -f em vez 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 com chmod apenas.

    
por 07.04.2013 / 20:13