¿Citar un nombre de archivo en un script pasado a chmod?

1

Tengo problemas para citar un nombre de archivo que finalmente se pasa a chmod . La secuencia de comandos se escribió originalmente para StrictModes y authorized_keys de SSH, pero tuve que expandirla debido a un UMASK malo que causó algunas molestias en el sistema de archivos. El guión está abajo, y su barco de producción produce:

chmod: /Users/<user>/Library/Application: No such file or directory
chmod: Support/TextMate/Managed/Bundles/HTML.tmbundle/Macros/Delete: No such file or directory
chmod: whitespace: No such file or directory
chmod: between: No such file or directory
chmod: tags.plist: No such file or directory
...
chmod: /Users/<user>/Library/Caches/com.operasoftware.Opera/Media: No such file or directory
chmod: Cache/index: No such file or directory
cut: stdin: Illegal byte sequence
...

He intentado algunas soluciones alternativas, pero ninguna de ellas me ha ayudado. Traté de hacer doble cita como ""$file"" , pero el problema persistió. También probé las garrapatas (que no se recomiendan en este caso), pero el problema persiste.

Lo más cercano a lo que realmente pude citar fue el "\"""$file""\"" disfuncional. Pero entonces chmod se quejó de que el nombre del archivo (con comillas) no era un archivo real:

$ sudo ~/fix-perms.sh
chmod: "/Users/Shared/.localized": No such file or directory
chmod: "/Users/<user>/.CFUserTextEncoding": No such file or directory
chmod: "/Users/<user>/.lesshst": No such file or directory

¿Cómo cito el nombre de archivo que sale de find que se pasa a chmod ? O ¿cómo obtengo chmod para tomar el nombre del archivo como un solo argumento?

$ cat ~/fix-perms.sh 
#!/bin/bash

# Directories
find /Users/* -type d -exec chmod 0700 {} \;
find /Users/Shared -type d -exec chmod 0777 {} \;

# Files
for file in 'find /Users/* -type f';
do
    if [ 'file "$file" | cut -d":" -f 2 | grep -i -c executable' -eq 0 ];
    then
        'chmod 0600 "$file"'
    else
        'chmod 0700 "$file"'
    fi
done

for user in 'ls -A /Users';
do
    if [ -e "/Users/$user/.ssh/authorized_keys" ];
    then
        chmod 0600 "/Users/${user}/.ssh/authorized_keys"
    fi
done
    
pregunta jww 26.08.2015 - 16:05

4 respuestas

1

Pude resolver el problema cambiando IFS a IFS=$(echo -en "\n\b") y no citando el nombre del archivo. IFS es Separador de campo interno , se utiliza (entre otros) para la división de palabras después de las expansiones de shell, e incluye un espacio por defecto.

Encontré el truco IFS en Shell BASH: para el archivo Loop Nombres con espacios .

$ cat fix-perms.sh
#!/bin/bash

SAVED_IFS=$IFS
IFS=$(echo -en "\n\b")

# Directories
chmod 0755 /Users
find /Users/* -type d -exec chmod 0700 {} \;
find /Users/Shared -type d -exec chmod 0777 {} \;

# Files
for file in 'find /Users/* -type f';
do
    if [ 'file "$file" | cut -d":" -f 2 | grep -i -c executable' -eq 0 ];
    then
        chmod 0600 "$file"
    else
        chmod 0700 "$file"
    fi
done

for user in 'ls -A /Users';
do
    if [ -e "/Users/$user/.ssh/authorized_keys" ];
    then
        chmod 0600 "/Users/${user}/.ssh/authorized_keys"
    fi
done

IFS=$SAVED_IFS
    
respondido por el jww 27.08.2015 - 20:44
3

fwiw, find [options] -print0 , cuando se usa junto con (canalizado a) xargs -0 [options] , maneja los nombres de los archivos con espacios, sin desordenar con for loops o IFS :

find /Users ! -path '/Users/Shared*' -type f -print0 | xargs -0 -I {} \
    bash -c 'if [[ "$(file -b --mime-type -- "{}")" = "application/x-mach-binary" ]]; then
                 chmod 700 "{}"
             else
                 chmod 600 "{}"
             fi'
    
respondido por el mtklr 30.08.2015 - 16:04
1

find puede manejar espacios en nombres de archivos. Tu for loops está causando los problemas. Puedes poner tu lógica en varios comandos find . No todos los directorios en la carpeta de inicio de un usuario deben ser accesibles solo para ellos. Público y sitios vienen a la mente. Puedes corregirlos por separado en otro comando de búsqueda.

find /Users/* ! -path '/Users/Shared*'  -type d ! \( -name Public -o -name Sites \) -exec chmod 0700 {} +

El comando excluye el directorio Compartido junto con los directorios Público y de Sitios y luego cambia los permisos en los directorios restantes.

Puedes usar + en tus declaraciones find para que find actúe como si fuera xargs .

find /Users/Shared -type d -exec chmod 0777 {} +

Suponiendo que los archivos ejecutables en la carpeta de inicio de un usuario son paquetes de aplicaciones, entonces podría hacer esto.

find /Users/* ! -path '/Users/Shared*' type f ! perm -755 -exec chmod 0600 {} +
    
respondido por el fd0 26.08.2015 - 18:19
1

La solución de mtklr (y Patrix) funcionará, pero me parece más sencillo usar un bucle while read cuando se trata de una lista de archivos de find ... -print0 :

find /Users '!' -path '/Users/Shared*' -type f -print0 |
    while IFS= read -d '' -r file; do
        if file "$file" | grep -iq ": .*executable"; then
            chmod 700 "$file"
        else
            chmod 600 "$file"
        fi
    done

read -d '' usa nulos como delimitador. También usé -r para evitar que intente analizar escapes, y IFS= para evitar que se recorte los espacios en blanco al final del nombre de archivo (y como es un prefijo para el comando read , solo se aplica a ese y no tienes que volver a configurarlo después.

Por cierto, también usé if ... | grep -q ... : -q le dice a grep que no genere nada, pero su estado de salida solo será exitoso si encuentra al menos una coincidencia. Y como if solo prueba el estado de salida del comando en él, eso es todo lo que se necesita.

Por cierto, esto se basa principalmente en BashFAQ # 20: ¿Cómo puedo encontrar y manejar con seguridad nombres de archivos que contengan nuevas líneas, espacios o ambos? . Recomiendo echarle un vistazo ...

    
respondido por el Gordon Davisson 31.08.2015 - 04:18

Lea otras preguntas en las etiquetas