Pola nama file Shell yang diperluas ke file dot tetapi tidak ke `..`?

13

Baru-baru ini saya mengalami kecelakaan kecil yang disebabkan oleh pola shell yang meluas secara tak terduga. Saya ingin mengubah pemilik sekelompok file dot di /rootdirektori, jadi saya lakukan

chown -R root .*

Secara alami, .*diperluas ..menjadi sedikit bencana.

Saya tahu dalam bashperilaku ini dapat diubah dengan men-tweak beberapa opsi shell, tetapi dengan pengaturan default apakah ada pola yang akan berkembang ke setiap file dot dalam direktori tetapi tidak ke .dan ..?

Ernest A
sumber

Jawaban:

20

Bash, ksh dan zsh memiliki solusi yang lebih baik, tetapi dalam jawaban ini saya menganggap shell POSIX.

Pola tersebut .[!.]*cocok dengan semua file yang dimulai dengan titik diikuti oleh karakter non-titik. (Catatan yang [^.]didukung oleh beberapa shell tetapi tidak semua, sintaks portabel untuk pelengkap set karakter dalam pola wildcard adalah [!.].) Oleh karena itu tidak termasuk .dan .., tetapi juga file yang dimulai dengan dua titik. Pola ini ..?*menangani file yang dimulai dengan dua titik dan bukan saja ...

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

Ini adalah pola klasik yang diatur untuk mencocokkan semua file:

* .[!.]* ..?*

Keterbatasan dari pendekatan ini adalah bahwa jika salah satu polanya tidak cocok, itu diberikan kepada perintah. Dalam skrip, saat Anda ingin mencocokkan semua file dalam direktori kecuali .dan .., ada beberapa solusi, semuanya rumit:

  • Gunakan * .*untuk menghitung semua entri, dan mengecualikan .dan ..dalam satu lingkaran. Satu atau kedua pola tersebut mungkin tidak cocok dengan apa pun, sehingga loop perlu memeriksa keberadaan setiap file. Anda memiliki kesempatan untuk menyaring kriteria lainnya; misalnya, hapus -htes jika Anda ingin melewatkan tautan simbolik yang menjuntai.

    for x in * .*; do
      case $x in .|..) continue;; esac
      [ -e "$x" ] || [ -h "$x" ] || continue
      somecommand "$x"
    done
  • Varian yang lebih kompleks di mana perintah dijalankan hanya sekali. Perhatikan bahwa parameter posisi musnah (shell POSIX tidak memiliki array); letakkan ini dalam fungsi terpisah jika ini merupakan masalah.

    set --
    for x in * .[!.]* ..?*; do
      case $x in .|..) continue;; esac
      [ -e "$x" ] || [ -h "$x" ] || continue
      set -- "$@" "$x"
    done
    somecommand "$@"
  • Gunakan * .[!.]* ..?*triptych. Sekali lagi, satu atau lebih pola mungkin tidak cocok, jadi kita perlu memeriksa file yang ada (termasuk tautan simbolik yang menggantung).

    for x in * .[!.]* ..?*; do
      [ -e "$x" ] || [ -h "$x" ] || continue
      somecommand "$x"
    done
  • Gunakan * .[!.]* ..?*tryptich, dan jalankan perintah sekali per pola tetapi hanya jika itu cocok dengan sesuatu. Ini menjalankan perintah hanya sekali. Perhatikan bahwa parameter posisi musnah (shell POSIX tidak memiliki array), letakkan ini dalam fungsi terpisah jika ini merupakan masalah.

    set -- *
    [ -e "$1" ] || [ -h "$1" ] || shift
    set -- .[!.]* "$@"
    [ -e "$1" ] || [ -h "$1" ] || shift
    set -- ..?* "$@"
    [ -e "$1" ] || [ -h "$1" ] || shift
    somecommand "$@"
  • Gunakan find. Dengan menemukan GNU atau BSD, menghindari rekursi mudah dengan opsi -mindepthdan -maxdepth. Dengan POSIX find, ini sedikit rumit, tetapi bisa dilakukan. Formulir ini memiliki kelebihan dengan mudah memungkinkan untuk menjalankan perintah satu kali alih-alih sekali per file (tetapi ini tidak dijamin: jika perintah yang dihasilkan terlalu panjang, perintah akan dijalankan dalam beberapa batch).

    find . -name . -o -exec somecommand {} + -o -type d -prune
Gilles 'SANGAT berhenti menjadi jahat'
sumber
6

Ini berfungsi jika semua nama file mengandung setidaknya tiga karakter (termasuk titik):

chown -R root .??*

Untuk solusi yang lebih kuat, Anda dapat menggunakan find:

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

sumber