Шаблон імені файлу оболонки, який розширюється на крапкові файли, але не на `..`?


13

Нещодавно у мене був невеликий збіг, спричинений малюнком оболонки, який несподівано розширився. Я хотів змінити власника купи точкових файлів у /rootкаталозі, так і зробив

chown -R root .*

Природно, .*розширення на ..яке було трохи катастрофою.

Я знаю, що в bashцій поведінці можна змінити, змінивши якусь опцію оболонки, але з налаштуваннями за замовчуванням є який-небудь шаблон, який би розширювався на кожен точковий файл у каталозі, але не на .і ..?

Відповіді:


20

Bash, ksh і zsh мають кращі рішення, але в цій відповіді я припускаю оболонку POSIX.

Шаблон .[!.]*відповідає всім файлам, які починаються з крапки, за якою йде неточний символ. (Зверніть увагу, що [^.]підтримується деякими оболонками, але не всіма. Портативний синтаксис для доповнення набору символів у шаблонах підстановок є [!.].) Тому він виключає .та .., але також і файли, що починаються з двох крапок. Шаблон ..?*обробляє файли, які починаються з двох крапок і є не просто ...

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

Це класичний шаблон, встановлений для відповідності всім файлам:

* .[!.]* ..?*

Обмеженням цього підходу є те, що якщо один із шаблонів нічого не відповідає, він передається команді. У сценарії, коли ви хочете зіставити всі файли в каталозі, за винятком .і .., є кілька рішень, усі вони громіздкі:

  • Використовуйте * .*для перерахування всіх записів, виключайте .і ..в циклі. Один або обидва шаблони можуть нічого не відповідати, тому цикл повинен перевірити наявність кожного файлу. Ви маєте можливість фільтрувати за іншими критеріями; наприклад, видаліть -hтест, якщо ви хочете пропустити звисаючі символічні посилання.

    for x in * .*; do
      case $x in .|..) continue;; esac
      [ -e "$x" ] || [ -h "$x" ] || continue
      somecommand "$x"
    done
    
  • Більш складний варіант, коли команда виконується лише один раз. Зверніть увагу, що позиційні параметри клобовані (оболонки POSIX не мають масивів); поставте це в окрему функцію, якщо це проблема.

    set --
    for x in * .[!.]* ..?*; do
      case $x in .|..) continue;; esac
      [ -e "$x" ] || [ -h "$x" ] || continue
      set -- "$@" "$x"
    done
    somecommand "$@"
    
  • Використовуйте * .[!.]* ..?*триптих. Знову ж таки, один або декілька шаблонів можуть нічого не відповідати, тому нам потрібно перевірити наявність наявних файлів (включаючи звисаючі символічні посилання).

    for x in * .[!.]* ..?*; do
      [ -e "$x" ] || [ -h "$x" ] || continue
      somecommand "$x"
    done
    
  • Використовуйте * .[!.]* ..?*триптих та виконайте команду один раз за шаблоном, але лише якщо він щось відповідає. Ця команда виконує лише один раз. Зверніть увагу на те, що позиційні параметри клобовані (в оболонках POSIX немає масивів), поставте це в окрему функцію, якщо це проблема.

    set -- *
    [ -e "$1" ] || [ -h "$1" ] || shift
    set -- .[!.]* "$@"
    [ -e "$1" ] || [ -h "$1" ] || shift
    set -- ..?* "$@"
    [ -e "$1" ] || [ -h "$1" ] || shift
    somecommand "$@"
    
  • Використовуйте find. З GNU або BSD знайти, уникнути рекурсії легко за допомогою варіантів -mindepthі -maxdepth. З POSIX знахідкою це трохи складніше, але це можна зробити. Ця форма має перевагу в тому, що легко дозволяє запускати команду одноразово, а не один раз на файл (але це не гарантується: якщо результуюча команда буде занадто довгою, команда буде виконуватися кількома партіями).

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

6

Це працює, якщо всі імена файлів містять щонайменше три символи (включаючи крапку):

chown -R root .??*

Для більш надійного рішення ви можете використовувати find:

find . -maxdepth 1 -name '.*' -exec chown -R root {} \;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.