Modèle de nom de fichier shell qui se développe en fichiers à points mais pas en «..»?

13

Récemment, j'ai eu un petit incident causé par un motif de coque qui s'est développé de manière inattendue. Je voulais changer le propriétaire d'un tas de fichiers dot dans le /rootrépertoire, donc je l'ai fait

chown -R root .*

Naturellement, l' .*expansion à ..laquelle était un peu un désastre.

Je sais que bashce comportement peut être modifié en modifiant certaines options du shell, mais avec les paramètres par défaut, existe-t-il un modèle qui s'étendrait à chaque fichier de points du répertoire, mais pas à .et ..?

Ernest A
la source

Réponses:

20

Bash, ksh et zsh ont de meilleures solutions, mais dans cette réponse, je suppose un shell POSIX.

Le modèle .[!.]*correspond à tous les fichiers qui commencent par un point suivi d'un caractère non point. (Notez que [^.]la syntaxe portable du complément de jeu de caractères dans les modèles génériques est prise en charge par certains shells mais pas par tous [!.].) Elle exclut donc .et .., mais aussi les fichiers commençant par deux points. Le modèle ..?*gère les fichiers qui commencent par deux points et ne sont pas seulement ...

chown -R root .[!.]* ..?*

Il s'agit du modèle classique défini pour correspondre à tous les fichiers:

* .[!.]* ..?*

Une limitation de cette approche est que si l'un des modèles ne correspond à rien, il est transmis à la commande. Dans un script, lorsque vous souhaitez faire correspondre tous les fichiers d'un répertoire, sauf .et .., il existe plusieurs solutions, toutes lourdes:

  • Utilisez * .*pour énumérer toutes les entrées et exclure .et ..dans une boucle. Un ou les deux modèles peuvent ne correspondre à rien, la boucle doit donc vérifier l'existence de chaque fichier. Vous avez la possibilité de filtrer sur d'autres critères; par exemple, supprimez le -htest si vous souhaitez ignorer les liens symboliques pendantes.

    for x in * .*; do
      case $x in .|..) continue;; esac
      [ -e "$x" ] || [ -h "$x" ] || continue
      somecommand "$x"
    done
  • Une variante plus complexe où la commande n'est exécutée qu'une seule fois. Notez que les paramètres de position sont encombrés (les shells POSIX n'ont pas de tableaux); mettez cela dans une fonction distincte si c'est un problème.

    set --
    for x in * .[!.]* ..?*; do
      case $x in .|..) continue;; esac
      [ -e "$x" ] || [ -h "$x" ] || continue
      set -- "$@" "$x"
    done
    somecommand "$@"
  • Utilisez le * .[!.]* ..?*triptyque. Encore une fois, un ou plusieurs des modèles peuvent ne rien correspondre, nous devons donc vérifier les fichiers existants (y compris les liens symboliques suspendus).

    for x in * .[!.]* ..?*; do
      [ -e "$x" ] || [ -h "$x" ] || continue
      somecommand "$x"
    done
  • Utilisez le * .[!.]* ..?*tryptich et exécutez la commande une fois par modèle, mais uniquement si elle correspond à quelque chose. Cela n'exécute la commande qu'une seule fois. Notez que les paramètres positionnels sont encombrés (les shells POSIX n'ont pas de tableaux), mettez cela dans une fonction distincte si c'est un problème.

    set -- *
    [ -e "$1" ] || [ -h "$1" ] || shift
    set -- .[!.]* "$@"
    [ -e "$1" ] || [ -h "$1" ] || shift
    set -- ..?* "$@"
    [ -e "$1" ] || [ -h "$1" ] || shift
    somecommand "$@"
  • Utilisez find. Avec GNU ou BSD find, éviter les récursions est facile avec les options -mindepthet -maxdepth. Avec POSIX find, c'est un peu plus compliqué, mais cela peut être fait. Ce formulaire a l'avantage de permettre facilement d'exécuter la commande une seule fois au lieu d'une fois par fichier (mais ce n'est pas garanti: si la commande résultante est trop longue, la commande sera exécutée en plusieurs lots).

    find . -name . -o -exec somecommand {} + -o -type d -prune
Gilles 'SO- arrête d'être méchant'
la source
6

Cela fonctionne si tous les noms de fichiers contiennent au moins trois caractères (y compris le point):

chown -R root .??*

Pour une solution plus robuste, vous pouvez utiliser find:

find . -maxdepth 1 -name '.*' -exec chown -R root {} \;

la source