Pattern di nome file Shell che si espande in file dot ma non in `..`?


13

Recentemente ho avuto un piccolo incidente causato da un modello di conchiglia che si è espanso in modo inaspettato. Volevo cambiare il proprietario di un gruppo di file dot nella /rootdirectory, così ho fatto

chown -R root .*

Naturalmente, l' .*espansione a ..cui è stato un po 'un disastro.

So che in bashquesto comportamento può essere modificato modificando alcune opzioni della shell, ma con le impostazioni predefinite c'è qualche modello che si espanderebbe in ogni file di punti nella directory ma non in .e ..?

Risposte:


20

Bash, ksh e zsh hanno soluzioni migliori, ma in questa risposta presumo una shell POSIX.

Il modello .[!.]*corrisponde a tutti i file che iniziano con un punto seguito da un carattere non punto. (Si noti che [^.]è supportato da alcune shell ma non tutte, la sintassi portatile per il complemento di set di caratteri è nei caratteri jolly [!.]). Pertanto, esclude .e .., ma anche i file che iniziano con due punti. Il modello ..?*gestisce i file che iniziano con due punti e non sono solo ...

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

Questo è il modello classico impostato per abbinare tutti i file:

* .[!.]* ..?*

Una limitazione di questo approccio è che se uno dei pattern non corrisponde a nulla, viene passato al comando. In uno script, quando si desidera abbinare tutti i file in una directory tranne .e .., ci sono diverse soluzioni, tutte ingombranti:

  • Utilizzare * .*per enumerare tutte le voci ed escludere .e ..in un ciclo. Uno o entrambi i pattern potrebbero non corrispondere a nulla, quindi il loop deve verificare l'esistenza di ciascun file. Hai la possibilità di filtrare su altri criteri; ad esempio, rimuovere il -htest se si desidera saltare i collegamenti simbolici sospesi.

    for x in * .*; do
      case $x in .|..) continue;; esac
      [ -e "$x" ] || [ -h "$x" ] || continue
      somecommand "$x"
    done
  • Una variante più complessa in cui il comando viene eseguito una sola volta. Si noti che i parametri posizionali sono bloccati (le shell POSIX non hanno matrici); mettere questo in una funzione separata se questo è un problema.

    set --
    for x in * .[!.]* ..?*; do
      case $x in .|..) continue;; esac
      [ -e "$x" ] || [ -h "$x" ] || continue
      set -- "$@" "$x"
    done
    somecommand "$@"
  • Usa il * .[!.]* ..?*trittico. Ancora una volta, uno o più dei motivi potrebbero non corrispondere a nulla, quindi è necessario verificare la presenza di file esistenti (inclusi collegamenti simbolici sospesi).

    for x in * .[!.]* ..?*; do
      [ -e "$x" ] || [ -h "$x" ] || continue
      somecommand "$x"
    done
  • Usa il * .[!.]* ..?*trittico ed esegui il comando una volta per modello, ma solo se corrisponde a qualcosa. Questo esegue il comando solo una volta. Nota che i parametri posizionali sono bloccati (le shell POSIX non hanno array), inseriscilo in una funzione separata se questo è un problema.

    set -- *
    [ -e "$1" ] || [ -h "$1" ] || shift
    set -- .[!.]* "$@"
    [ -e "$1" ] || [ -h "$1" ] || shift
    set -- ..?* "$@"
    [ -e "$1" ] || [ -h "$1" ] || shift
    somecommand "$@"
  • Usa find. Con GNU o BSD find, evitare la ricorsione è facile con le opzioni -mindepthe -maxdepth. Con POSIX find, è un po 'più complicato, ma può essere fatto. Questo modulo ha il vantaggio di consentire facilmente di eseguire il comando una sola volta anziché una volta per file (ma questo non è garantito: se il comando risultante è troppo lungo, il comando verrà eseguito in più batch).

    find . -name . -o -exec somecommand {} + -o -type d -prune

6

Funziona se tutti i nomi dei file contengono almeno tre caratteri (incluso il punto):

chown -R root .??*

Per una soluzione più solida, è possibile utilizzare find:

find . -maxdepth 1 -name '.*' -exec chown -R root {} \;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.