Comment faire pour récursivement chmod tous les répertoires sauf les fichiers?

Réponses:

802

Pour donner récursivement aux répertoires des privilèges de lecture et d'exécution:

find /path/to/base/dir -type d -exec chmod 755 {} +

Pour donner récursivement aux fichiers des privilèges de lecture:

find /path/to/base/dir -type f -exec chmod 644 {} +

Ou, s'il y a beaucoup d'objets à traiter:

chmod 755 $(find /path/to/base/dir -type d)
chmod 644 $(find /path/to/base/dir -type f)

Ou, pour réduire le chmodfrai:

find /path/to/base/dir -type d -print0 | xargs -0 chmod 755 
find /path/to/base/dir -type f -print0 | xargs -0 chmod 644
nik
la source
7
Les deux premiers exemples ne pour les répertoires avec trop de fichiers: -bash: /bin/chmod: Argument list too long. La dernière commande fonctionne avec de nombreux fichiers, mais si vous en utilisez sudoune, vous devez faire attention à la mettre avant xargs au lieu de chmod:find /path/to/base/dir -type d -print0 | sudo xargs -0 chmod 755
Agargara
2
A noter également que ces commandes incluent le répertoire de base. Ainsi, dans l'exemple ci-dessus, dirsera également défini sur 755.
CenterOrbit le
2
chmod ... $(find /path/to/base/dir -type ...)échoue pour les noms de fichiers avec des espaces dans le nom.
Dan Dascalescu
7
Je pense que la version la plus correcte (mais pas la plus rapide) en ce qui concerne les espaces et les symboles dans les noms de fichiers et le nombre de fichiers est find /path/to/base/dir -type d -exec chmod 755 {} \;( find /path/to/base/dir -type f -exec chmod 644 {} \;).
Peter K
Notez que cela ne concerne que la première couche qu'il peut lire. Vous devrez l'exécuter plusieurs fois pour aller plus loin dans l'arborescence.
Henk Poley le
291

Une raison commune pour ce genre de chose est de définir les répertoires sur 755 mais les fichiers sur 644. Dans ce cas, il existe un moyen légèrement plus rapide que l' findexemple de nik :

chmod -R u+rwX,go+rX,go-w /path

Sens:

  • -R = récursivement;
  • u+rwX = Les utilisateurs peuvent lire, écrire et exécuter.
  • go+rX = le groupe et les autres peuvent lire et exécuter;
  • go-w = le groupe et les autres ne peuvent pas écrire

La chose importante à noter ici est que les majuscules Xagissent différemment des minuscules x. Dans le manuel, nous pouvons lire:

Les bits execute / search si le fichier est un répertoire ou si l'un des bits execute / search est défini dans le mode d'origine (non modifié).

En d'autres termes, chmod u + X sur un fichier ne définira pas le bit d'exécution; et g + X ne le définira que s'il est déjà défini pour l'utilisateur.

bosse
la source
5
-R = récursivement; u + rwX = Les utilisateurs peuvent lire, écrire et exécuter; go + rX = groupe et les autres peuvent lire et exécuter; go-w = groupe et les autres ne peuvent pas écrire
släcker
23
Ce modèle ne résoudra pas le problème lorsque quelqu'un l'aura fait chmod -R 777puisque l' +Xoption ne réinitialisera pas les bits d'exécution existants sur les fichiers. L'utilisation de -x réinitialisera les répertoires et empêchera d'y descendre.
Andrew Vit
3
@ ring0: Je n'ai pas l'intention de répondre à la question telle qu'elle a été posée - Nikk l'a déjà parfaitement fait. Je propose une solution moins chère pour le cas le plus courant. Et oui, vous obtenez différentes autorisations pour les fichiers et les répertoires X, comme expliqué dans les commentaires.
Bobince
6
go+rX,go-w-> go=rXn'est ce pas?
Pierre de LESPINAY
5
Vous pouvez également utiliser la chmod u-x,u+Xcombinaison, etc., pour supprimer les bits d'exécution des fichiers, mais les ajouter pour les répertoires.
w0rp
14

Si vous voulez vous assurer que les fichiers sont définis sur 644 et que certains fichiers du chemin contiennent l'indicateur d'exécution, vous devez d'abord supprimer l'indicateur d'exécution. + X ne supprime pas l'indicateur d'exécution des fichiers qui l'ont déjà.

Exemple:

chmod -R ugo-x,u+rwX,go+rX,go-w path

Mise à jour: cela semble échouer car la première modification (ugo-x) rend le répertoire non exécutable, de sorte que tous les fichiers qu'il contient ne sont pas modifiés.

mpolden
la source
1
Cela fonctionne pour moi et je ne vois pas pourquoi cela ne le ferait pas. (Bien sûr, si vous veniez de le faire chmod -R ugo-x path, cela pourrait être un problème. Mais la commande complète fera le chmod u+rwXsur chaque répertoire avant d'essayer d'y descendre.) Cependant, je pense que cela chmod R u=rw,go=r,a+X pathsuffit - et que c'est plus court.
Scott
J'ai trouvé que cela fonctionnait correctement. il n'y avait aucun problème à entrer dans les répertoires
Someone Somewhere
4

J'ai décidé d'écrire un petit script pour cela moi-même.

Script chmod récursif pour les répertoires et / ou fichiers - Gist :

chmodr.sh

#!/bin/sh
# 
# chmodr.sh
#
# author: Francis Byrne
# date: 2011/02/12
#
# Generic Script for recursively setting permissions for directories and files
# to defined or default permissions using chmod.
#
# Takes a path to recurse through and options for specifying directory and/or 
# file permissions.
# Outputs a list of affected directories and files.
# 
# If no options are specified, it recursively resets all directory and file
# permissions to the default for most OSs (dirs: 755, files: 644).

# Usage message
usage()
{
  echo "Usage: $0 PATH -d DIRPERMS -f FILEPERMS"
  echo "Arguments:"
  echo "PATH: path to the root directory you wish to modify permissions for"
  echo "Options:"
  echo " -d DIRPERMS, directory permissions"
  echo " -f FILEPERMS, file permissions"
  exit 1
}

# Check if user entered arguments
if [ $# -lt 1 ] ; then
 usage
fi

# Get options
while getopts d:f: opt
do
  case "$opt" in
    d) DIRPERMS="$OPTARG";;
    f) FILEPERMS="$OPTARG";;
    \?) usage;;
  esac
done

# Shift option index so that $1 now refers to the first argument
shift $(($OPTIND - 1))

# Default directory and file permissions, if not set on command line
if [ -z "$DIRPERMS" ] && [ -z "$FILEPERMS" ] ; then
  DIRPERMS=755
  FILEPERMS=644
fi

# Set the root path to be the argument entered by the user
ROOT=$1

# Check if the root path is a valid directory
if [ ! -d $ROOT ] ; then
 echo "$ROOT does not exist or isn't a directory!" ; exit 1
fi

# Recursively set directory/file permissions based on the permission variables
if [ -n "$DIRPERMS" ] ; then
  find $ROOT -type d -print0 | xargs -0 chmod -v $DIRPERMS
fi

if [ -n "$FILEPERMS" ] ; then
  find $ROOT -type f -print0 | xargs -0 chmod -v $FILEPERMS
fi

Il fait essentiellement le chmod récursif, mais offre également un peu de flexibilité pour les options de ligne de commande (définit les autorisations de répertoire et / ou de fichier, ou les exclut, il réinitialise automatiquement tout à 755-644). Il vérifie également quelques scénarios d'erreur.

J'ai aussi écrit à ce sujet sur mon blog .

francisbyrne
la source
2

Pour donner récursivement aux répertoires des privilèges de lecture et d'exécution:

find /path/to/base/dir -type d -exec chmod 755 {} \;

Pour donner récursivement aux fichiers des privilèges de lecture:

find /path/to/base/dir -type f -exec chmod 644 {} \;

Mieux vaut tard que de ne jamais me laisser améliorer la réponse de nik du point de vue de l'exactitude. Ma solution est plus lente, mais elle fonctionne avec n'importe quel nombre de fichiers, avec n'importe quel symbole dans les noms de fichiers, et vous pouvez l'exécuter avec sudo normalement (mais méfiez-vous, elle pourrait détecter différents fichiers avec sudo).

Peter K
la source
Ceci est une dégradation de la réponse de Nik . Pourquoi croyez-vous qu'il y a quelque chose qui cloche dans la réponse de Nik?
Scott
@ Scott, la réponse de Nik échoue avec un grand nombre de fichiers.
Peter K
Je suis sûr à 99% que vous vous trompez. Pouvez-vous fournir des preuves à l'appui de votre demande?
Scott
Oui, il semble que je me trompe vraiment. find ... + semble casser la ligne en plusieurs commandes, belle prise!
Peter K
0

Essayez ce script python; il ne nécessite aucune génération de processus et ne fait que deux appels système par fichier. Mis à part une implémentation en C, ce sera probablement le moyen le plus rapide de le faire (j'en avais besoin pour réparer un système de fichiers de 15 millions de fichiers, qui étaient tous définis à 777)

#!/usr/bin/python3
import os
for par, dirs, files in os.walk('.'):
    for d in dirs:
        os.chmod(par + '/' + d, 0o755)
    for f in files:
        os.chmod(par + '/' + f, 0o644)

Dans mon cas, un try / catch était requis autour du dernier chmod, car certains fichiers spéciaux ont échoué.

mic_e
la source
-1

Vous pouvez également utiliser tree:

tree -faid /your_directory | xargs -L1 -I{} bash -c 'sudo chmod 755 "$1"' -- '{}'

et si vous souhaitez également afficher le dossier, ajoutez un écho

 tree -faid /your_directory | xargs -L1 -I{} bash -c 'sudo chmod 755 "$1" && echo$1' -- '{}'
Eduard Florinescu
la source
@Scott 1) ​​Vous avez raison sur + x j'ai changé pour 755; 2) 3) pour résoudre ceci, je mets l'espace réservé en guillemet simple comme ceci '{}'
Eduard Florinescu
@ Scott Je conviens que ce n'est pas la meilleure réponse, que ce soit lent, mais que nous allons partir d'ici à des fins "didactiques" et que les commentaires expliqueront plus en détail, de même que les gens peuvent en apprendre davantage sur les xargsproblèmes. Les guillemets simples dans les noms de fichiers sont eux-mêmes un problème pour de nombreuses commandes et scripts, c'est pourquoi j'ai énuméré tous les fichiers contenant des guillemets simples et les ai supprimés (les guillemets que je veux dire)
Eduard Florinescu
@Scott Sur mes systèmes, j'ai recherché tous les fichiers contenant des guillemets simples et remplaçant les guillemets simples
Eduard Florinescu 10/10
@Scott Comment corrigeriez-vous le fait que xargs ne résout pas correctement les guillemets simples?
Eduard Florinescu
Continuons cette discussion sur le chat .
Eduard Florinescu
-2

Vous pouvez utiliser le script bash suivant à titre d'exemple. Assurez-vous de lui donner des autorisations exécutables (755). Utilisez simplement ./autochmod.sh pour le répertoire en cours ou ./autochmod.sh <dir> pour en spécifier un autre.

#!/bin/bash

if [ -e $1 ]; then
    if [ -d $1 ];then
        dir=$1
    else
        echo "No such directory: $1"
        exit
    fi
else
    dir="./"
fi

for f in $(ls -l $dir | awk '{print $8}'); do
    if [ -d $f ];then
        chmod 755 $f
    else
        chmod 644 $f
    fi
done
utilisateur26528
la source
2
Hou la la! Tant de problèmes! (1) Si $1n'est pas null, mais n'est pas le nom d'un répertoire (par exemple, s'il s'agit d'une faute de frappe), il direst alors défini sur .sans message. (2) $1devrait être "$1"et $dirdevrait être "$dir". (3) vous n'avez pas besoin de dire "./"; "."c'est bien (et, à proprement parler, vous n'avez pas besoin de citations ici). (4) Ce n'est pas une solution récursive. (5) Sur mon système, ls -l … | awk '{ print $8 }'obtient les temps de modification des fichiers. Vous devez { print $9 }obtenir le premier mot du nom du fichier. Et même dans ce cas (6), cela ne gère pas les noms de fichiers avec des espaces. …
Scott
1
… Et dernier point () si ce script est dans le répertoire en cours, il passera chmod lui - même à 644, se rendant ainsi non-exécutable!
Scott