Используйте расширенные шаблоны глоббинга, чтобы перечислить файлы, но не директории в bash.

Вопрос или проблема

Допустим, у меня есть следующая структура директорий:

.
├── 11
├── 111
├── 112
├── 1121
├── 113
├── 11a
├── 11a1
├── 1a1
├── 1a2
├── 1aa1.png
├── 2a1
├── a.
├── a1a
├── a1a.jpg
├── a2a
├── aa
├── -aa
├── aa.gif
├── aa.jpg
├── aa.png
├── aa.tiff
├── a.exe
├── a.gif
├── a.html
├── a.jpg
├── a.png
├── a.tiff
├── b2
├── b2a
├── ba.gif
├── ba.jpg
├── ba.png
├── ba.tiff
├── b.html
├── cb1.png
├── d.gif
└── sub1
    ├── d.gif
    └── sub2

Итак, если я хочу совпасть со всем в . ($PWD), кроме файлов с расширением “.jpg”, я делаю следующее:

ls !(*.jpg)

Но он выводит:

11   1121  11a1  1aa1.png  a1a  aa.gif   a.exe   a.png   b2a     ba.tiff  d.gif
111  113   1a1   2a1       a2a  aa.png   a.gif   a.tiff  ba.gif  b.html   .gif
112  11a   1a2   a.        aa   aa.tiff  a.html  b2      ba.png  cb1.png

sub1:
.  ..  d.gif  sub2

Я хотел бы исключить и sub1, и все его содержимое (даже директории), то есть:

sub1:
.  ..  d.gif  sub2

Я могу исключить, используя:

GLOBIGNORE='sub1' перед ls, но что произойдет, если у меня есть больше директорий с разными именами?

Есть ли способ сделать это?

Обязательно ли, чтобы оболочка была bash? С ZSH и установленным EXTENDED_GLOB можно использовать (.) для квалификации () глобального выражения только обычными файлами .:

$ PS1='%% ' zsh -f
% setopt EXTENDED_GLOB
% mkdir foo && cd foo
% touch nope.jpg
% mkdir sub1
% touch asdf
% print ^*.jpg
asdf sub1
% print ^*.jpg(.)
asdf
% 

Решение:

ls -pd !(*.jpg) | grep -Ev /$

Детали

Ваш шаблон глоба не спускается в поддиректории. ls делает это: если вы передаете директорию в командной строке, он отображает содержимое этой директории.

Замените ls на echo:

$ echo !(*.jpg)

Или почти эквивалентное решение: укажите ls не спускаться в директории.

$ ls -d !(*.jpg)

Чтобы ответить на вопрос в комментариях: “как использовать команду ls для перечисления только файлов, а не папок?”, есть несколько вариантов:

Краткий способ перечислить только директории:

echo */

Так что, возможно, ответ для перечисления только файлов и не директорий может использовать grep -v

$ ls -p | grep -Ev /$

$ find . -maxdepth 1 ! -type d

Обратите внимание, что find будет перечислять скрытые файлы. И find . -type d (также) не будет следовать по симлинкам.

Или прагматичный (posix) цикл:

$ for f in * ; do if [ -f "$f" ] ; then echo "$f" ; fi ; done

Конечно, конкретное решение для расширения с отрицанием должно выглядеть так:

$ ls -pd !(*.jpg) | grep -Ev /$
11
111
112
1121
113
11a
11a1
1a1
1a2
1aa1.png
2a1
a.
a1a
a2a
aa
aa.gif
aa.png
aa.tiff
a.exe
a.gif
a.html
a.png
a.tiff
b2
b2a
ba.gif
ba.png
ba.tiff
b.html
cb1.png
d.gif

Примечание:

Опция -p вместе с grep делает трюк для идентификации директорий, посмотрите на страницу руководства ls:

-p, –file-type, –indicator-style=file-type

Вы можете указать шаблоны, которые хотите исключить, разделенные знаком | (в этом случае sub1) следующим образом:

ls !(*.jpg|sub1)

или вы можете использовать что-то вроде ls !(*.jpg|s*)

Будьте внимательны со пробелами между вертикальными линиями.

Я могу исключить, используя:
GLOBIGNORE=’sub1′ перед ls, но что произойдет, если у меня есть больше директорий с разными именами?

Из руководства по Bash, GLOBIGNORE это:

Список шаблонов, разделенных двоеточием, определяющий набор имен файлов, которые будут игнорироваться расширением имени файла. Если имя файла, соответствующее шаблону расширения имени файла, также соответствует одному из шаблонов в GLOBIGNORE, оно удаляется из списка совпадений. Сопоставление шаблона учитывает настройку опции оболочки extglob.

Вы можете направить вывод ls в фильтр grep следующим образом:

ls * | grep -v "jpg"

Чтобы вернуть все совпадения, которые не содержат строку “jpg”. (Флаг grep -v инвертирует совпадение, возвращая каждую строку, которая не содержит указанную строку.)

.

Ответ или решение

Вопрос о том, как использовать расширенное глобальное совпадение в Bash для получения списка файлов, исключая каталоги, касается важной задачи в администрировании систем и автоматизации процессов. Представим, что у вас есть определенная структура каталогов, как описано в примерах, и вам необходимо выбрать только файлы, исключая определенные шаблоны и избегая входа в подкаталоги.

Теория: В Unix-подобных системах, таких как Linux, командная оболочка Bash предлагает мощные возможности для управления файлами и каталогами с помощью глоббинговых шаблонов (glob patterns). Расширенные глоббинг (extglob) позволяет создать более гибкие правила для выбора файлов и каталогов, используя синтаксис с помощью дополнительных шаблонов. Однако простое использование команды ls с расширенным глоббингом может привести к нежелательному включению подкаталогов, а также возможно потребуется исключать определенные шаблоны, такие как файлы с расширением .jpg.

Пример: Предположим, что вы используете команду ls !(*.jpg) для исключения файлов с расширением .jpg. Однако это приводит к включению подкаталогов, таких как sub1, при отображении. Чтобы решить эту проблему и корректно исключить директории и их содержимое, можно использовать дополнительные инструменты.

Применение: Для эффективного решения задачи есть несколько подходов, которые можно использовать в зависимости от конкретной цели и ограничений.

  1. Использование extglob и дополнительных команд:
    Чтобы исключить подкаталоги и их содержимое, можно модифицировать команду ls с использованием -d для предотвращения входа в каталоги:

    shopt -s extglob
    ls -pd !(*.jpg) | grep -Ev '/$'

    Здесь -p добавляет символ / к именам каталогов, что позволяет затем использовать grep -Ev '/$' для фильтрации только файлов, исключая каталоги.

  2. Использование GLOBIGNORE:
    Переменная GLOBIGNORE в Bash позволяет исключать определенные файлы и каталоги из глоббинга. Вы можете задать ее значение перед выполнением команды:

    export GLOBIGNORE='sub1:*.jpg'
    ls -d */
    unset GLOBIGNORE

    Однако, эта техника может быть менее эффективной, если у вас большое количество различных имен для исключения.

  3. Использование цикла:
    Более комплексное решение может включать использование цикла for, чтобы пройтись по каждому элементу и проверить, является ли он файлом:

    for f in *; do
     if [ -f "$f" ]; then
       echo "$f"
     fi
    done

    Это решение особенно полезно, если вы хотите иметь больше контроля над дополнительными фильтрациями и действиями.

  4. Комбинирование с find:
    Для тех, кто предпочитает более универсальные инструменты, команда find предлагает мощное средство для фильтрации на основе типа файла и других атрибутов:

    find . -maxdepth 1 -type f ! -name "*.jpg"

    Здесь -maxdepth 1 гарантирует, что команда будет работать только на уровне текущего каталога, а не углубляться в подкаталоги.

Заключение: Выбирая инструмент и подход к решению задачи, нужно учитывать специфику вашей задачи и удобство использования. Bash, благодаря своим возможностям, предлагает различные способы решения такой задачи, и выбор наиболее подходящего — это дело предпочтений и потребностей системы. Использование расширенного глоббинга в сочетании с командами фильтрации позволяет гибко управлять содержимым каталогов, исключая из вывода ненужные элементы.

Оцените материал
Добавить комментарий

Капча загружается...