Convierta el formato de salida ls -l al formato chmod

17

Digamos que tengo el siguiente resultado de ls -l:

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

¿Cómo puedo convertir esto automáticamente al formato utilizado por chmod?

Por ejemplo:

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

Estoy usando OS X 10.8.3.

Tyilo
fuente
2
Mucho más fácil con stat. ¿Lo tienes? (Es una herramienta GNU, por lo que en su mayoría disponibles en Linux, no en Unix.)
manatwork
@manatwork stat fooda 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. No lo veo 755en eso.
Tyilo

Respuestas:

24

Algunos sistemas tienen comandos para mostrar los permisos de un archivo como un número, pero desafortunadamente, nada portátil.

zshtiene un stat(aka zstat) incorporado en el statmódulo:

zmodload zsh/stat
stat -H s some-file

Entonces, el modeestá en $s[mode]pero es el modo, es decir, tipo + permisos.

Si desea los permisos expresados ​​en octal, necesita:

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

Los BSD (incluido Apple OS / X ) también tienen un statcomando.

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

GNU find (desde 1990 y probablemente antes) puede imprimir los permisos como octal:

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

Más tarde (2001, mucho después zsh stat(1997) pero antes de BSD stat(2002)) statse introdujo un comando GNU con una sintaxis diferente:

stat -c %a some-file

Mucho antes de eso, IRIX ya tenía un statcomando (ya en IRIX 5.3 en 1994) con otra sintaxis:

stat -qp some-file

Nuevamente, cuando no hay un comando estándar, la mejor opción para la portabilidad es usar perl:

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

Puede pedirle a GNU statque muestre los permisos en formato octal utilizando la -copción 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

Entonces en tu 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

O incluso puede automatizarlo formateando statla salida 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

La solución anterior también funcionará para varios archivos si usa un comodín:

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

Funcionará correctamente con los nombres de archivo que contienen caracteres de espacio en blanco, pero fallará en los nombres de archivo que contengan comillas simples.

hombre trabajando
fuente
2
Mi statno tiene una -copción. Estoy usando OS X 10.8.3.
Tyilo
Gracias por la información, @Tyilo. Y lo siento, no puedo ayudar con las herramientas de OS X.
manatwork
Intente leer la página de manual ^ W ^ W ^ W stat (1) en Mac OS X tiene el indicador -f para especificar el formato de salida, por ejemplostat -f 'chmod %p "%N"'
gelraen
11

Para convertir de la notación simbólica a octal, una vez se me ocurrió :

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

Eso devuelve el número octal de la salida de ls -lun archivo.

$ echo 'drwSr-sr-T' | chmod_format
7654
Stéphane Chazelas
fuente
Usé esto en la salida de dpkgpara establecer los permisos de nuevo a "como instalado". Gracias por responder la pregunta literal sin tener en cuenta qué comando produjo la cadena de permiso.
HiTechHiTouch
3

Este comando en Mac bajo sh

stat -f "%Lp %N" your_files

si solo desea el permiso numérico, use solo% Lp.

por ejemplo:

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

El 700 es el permiso numérico que se puede usar en chmod, y Desktop es el nombre del archivo.

TonyL2ca
fuente
2

Aquí hay una respuesta a la pregunta Y (ignorando la pregunta X ), inspirada en el intento del 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

Lo anterior contiene algunos bashismos. La siguiente versión parece ser compatible con 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:

  • El LC_COLLATE=Cle dice al shell que trate los patrones de rango de secuencia de letras como si usaran el orden ASCII, por lo que [a-e]es equivalente a [abcde]. En algunos entornos locales (p. Ej., En_US), [a-e]es equivalente a [aAbBcCdDeE] (es decir, [abcdeABCDE]) o tal vez [abcdeABCD], vea ¿Por qué la declaración de caso bash no distingue entre mayúsculas y minúsculas ...? )
  • En la segunda versión (la que cumple con POSIX):

    • La primera casedeclaración podría reescribirse:

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

      pero creo que lo que tengo ahora hace que sea más fácil ver que l la carta se maneja de manera diferente. Alternativamente, podría reescribirse:

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

      ya que r, w, x, s, y tson las únicas cartas que nunca deben aparecer en una cadena de modo (que no sea l).

    • La segunda casedeclaración podría reescribirse:

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

      para hacer cumplir la regla de que solo las letras son válidas para especificar bits de modo. (Por el contrario, la versión más sucinta en el script completo es perezosa y aceptará -rw@rw#rw%como equivalente a  rwSrwSrwT). Alternativamente, podría reescribirse:

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

      ya que S, s, T, t, L, y lson las únicas cartas que nunca deben aparecer en una cadena modo (excepto r, wy 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

Y sí, sé que es mejor no usar echocon texto que pueda comenzar -; Solo quería copiar el ejemplo de uso de la pregunta. Tenga en cuenta, obviamente, que esto ignora el carácter 0 (es decir, el d/ b/ c/ -/ l/ p/ s/ D) y el 10 ( +/ ./ @). Se supone que los mantenedores de lsnunca definirán r/ Ro w/ Wcomo caracteres válidos en la tercera, sexta o novena posición (y, si lo hacen, deberían ser golpeados con palos ).


Además, acabo de encontrar el siguiente código, por cas , en Cómo restaurar la propiedad predeterminada del grupo / usuario de todos los archivos en / 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 ))

He probado este código (pero no a fondo), y parece funcionar, excepto por el hecho de que no reconoce lo Len la sexta posición. Sin embargo, tenga en cuenta que, si bien esta respuesta es superior en términos de simplicidad y claridad, la mía es en realidad más corta (contando solo el código dentro del bucle; el código que maneja una sola -rwxrwxrwxcadena, sin contar comentarios), y podría hacerse aún más corto reemplazando con .if condition; then …condition && …


Por supuesto, no debe analizar la salida dels .

Scott
fuente
@ StéphaneChazelas: OK, dije #!/bin/shy luego usé algunos bashismos. Ups Pero te perdiste un par: y tampoco pareces ser POSIX (el Estándar no menciona nada, y está ardiendo y ). Por el contrario, yo no uso ; eso aparece solo en la respuesta de cas , que cité desde aquí . Además, mi respuesta maneja 'l' y 'L', y ya señalé el hecho de que la respuesta de cas no. $(( variable++ ))$(( number ** number ))**++-- [[…]]
Scott
Perdón por l / l [[, leí demasiado rápido. Sí, y ++ no son POSIX. POSIX permite que los shells los implementen, eso significa que tiene que escribir $((- -a))si desea una doble negación, no que pueda usar $((--a))para referirse a una operación de disminución.
Stéphane Chazelas
Tenga en cuenta que seqno es un comando POSIX. Es posible que pueda usar el operador $ {var #?} Para evitar expr. No es que LC_COLLATE no anule LC_ALL
Stéphane Chazelas
@ StéphaneChazelas: OK, ahora estás hablando de la respuesta de Cas, ¿verdad? Está tomando el enfoque "vago" de usar la aritmética decimal para construir una cadena que se parece a un número octal. Tenga en cuenta que todos sus valores de paso (4000, 2000, 1000, 400, 200, 100, 40, 20 y 10) son números decimales. Pero, dado que no hay 8's' o 9's, y no hay forma de obtener más que 7en cualquier posición decimal, puede tirar de la farsa. ... ... ... ... ... ... ... (Este comentario es una respuesta a un comentario de Stéphane Chazelas que desapareció)
Scott
Sí, me di cuenta más tarde, por eso eliminé el comentario.
Stéphane Chazelas
1

Si su objetivo es tomar permisos de un archivo y darlos también a otro, GNU chmodya tiene una opción de "referencia" para eso .

Bratchley
fuente
El OP mencionó que estaba en Apple OS / X, por chmodlo que no será el GNU chmodallí.
Stéphane Chazelas
Ah sí, estoy viendo el comentario en la otra respuesta donde dicen su plataforma. Sin embargo, el mío no es el único que menciona GNU y puedes obtener GNU Utilities en Mac OS X
Bratchley
0

Una alternativa, si desea guardar los permisos, para restaurarlos más adelante, o en un archivo diferente es usar setfacl/getfacl, y también restaurará las ACL (borrador POSIX) como un bono.

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

(en Solaris, use en -flugar de -M).

Sin embargo, aunque están disponibles en algunos BSD, no están en Apple OS / X donde chmodsolo se manipulan las ACL .

Stéphane Chazelas
fuente
0

En Mac OS X (10.6.8) tiene que usar stat -f format(porque en realidad es 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 traducir simplemente una cadena de permisos simbólicos producida por ls -len octal (usando solo shell incorporado) ver: showperm.bash .

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