Моя відповідь tldr така:
function emptydir {
[ "$1/"* "" = "" ] 2> /dev/null &&
[ "$1/"..?* "" = "" ] 2> /dev/null &&
[ "$1/".[^.]* "" = "" ] 2> /dev/null ||
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
Він сумісний з POSIX, і це не так важливо, як правило, це швидше, ніж рішення, яке перераховує каталог та передає вихід на греп.
Використання:
if emptydir adir
then
echo "nothing found"
else
echo "not empty"
fi
Мені подобається відповідь https://unix.stackexchange.com/a/202276/160204 , яку я переписую як:
function emptydir {
! { ls -1qA "./$1/" | grep -q . ; }
}
У ньому перераховано каталог та передано результат на отримання грепа. Натомість я пропоную просту функцію, яка ґрунтується на глобальному розширенні та порівнянні.
function emptydir {
[ "$(shopt -s nullglob; echo "$1"/{,.[^.],..?}*)" = "" ]
}
Ця функція не є стандартною POSIX і викликає передплату за допомогою $(). Спочатку я пояснюю цю просту функцію, щоб пізніше зрозуміти остаточне рішення (див. Відповідь tldr вище).
Пояснення:
Ліва сторона (LHS) порожня, коли не відбувається розширення, що є випадком, коли каталог порожній. Опція nullglob потрібна, оскільки в іншому випадку, коли немає відповідності, сам глобус є результатом розширення. (Якщо RHS відповідає глобусам LHS, коли каталог порожній, не працює через помилкові позитиви, які виникають, коли глобус LHS відповідає одному файлу, названому як сам глобус: *the glob відповідає збігу підрядків *у імені файлу. ) Вираз дужки {,.[^.],..?}охоплює приховані файли, але не ..або ..
Оскільки shopt -s nullglobвиконується всередині $()(підпакет), це не змінює nullglobпараметр поточної оболонки, що зазвичай є хорошою справою. З іншого боку, корисно встановити цю опцію в сценаріях, оскільки помилка схильна, щоб глобус повертав щось, коли немає відповідності. Отже, можна встановити параметр nullglob на початку сценарію, і він не знадобиться у функції. Пам'ятайте про це: ми хочемо рішення, яке працює з параметром nullglob.
Застереження:
Якщо у нас немає доступу до читання до каталогу, функція повідомляє так само, як якщо б там був порожній каталог. Це стосується також і функції, яка перераховує каталог та переглядає вихід.
shopt -s nullglobКоманда не є стандартом POSIX.
Він використовує підшару, створену компанією $(). Це не велика справа, але приємно, якщо ми можемо цього уникнути.
Про:
Не те, що це насправді має значення, але ця функція в чотири рази швидша за попередню, вимірюється кількістю часу процесора, проведеного в ядрі в процесі.
Інші рішення:
Ми можемо видалити нони POSIX shopt -s nullglobкоманди на LHS і помістіть рядок "$1/* $1/.[^.]* $1/..?*"в RHS і усунути окремо помилкові спрацьовування , які виникають , коли у нас є тільки файли з ім'ям '*', .[^.]*або ..?*в каталозі:
function emptydir {
[ "$(echo "$1"/{,.[^.],..?}*)" = "$1/* $1/.[^.]* $1/..?*" ] &&
[ ! -e "$1/*" ] && [ ! -e "$1/.[^.]*" ] && [ ! -e "$1/..?*" ]
}
Без shopt -s nullglobкоманди тепер має сенс видалити нижню частину корпусу, але ми повинні бути обережними, оскільки ми хочемо уникнути розбиття слів і все ж дозволити розширення глобальної версії на LHS. Зокрема, цитування, щоб уникнути розбиття слів, не працює, оскільки це також запобігає розширенню глобальної сфери. Наше рішення - розглянути глобуси окремо:
function emptydir {
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
У нас ще є розділення слів для окремих глобусів, але це нормально зараз, оскільки це призведе до помилки лише тоді, коли каталог не буде порожнім. Ми додали 2> / dev / null, щоб відкинути повідомлення про помилку, коли на LHS є багато файлів, що відповідають даному глобусу.
Ми пам'ятаємо, що ми хочемо рішення, яке також працює з параметром nullglob. Вищевказане рішення не відповідає параметру nullglob, оскільки коли каталог порожній, LHS також порожній. На щастя, ніколи не сказано, що каталог порожній, коли його немає. Лише не можна сказати, що вона порожня, коли є. Отже, ми можемо керувати параметром nullglob окремо. Ми не можемо просто додати справи [ "$1/"* = "" ]тощо, тому що вони розширяться як [ = "" ]і т.д., які є синтаксично неправильними. Отже, ми використовуємо [ "$1/"* "" = "" ]натомість тощо. Ми знову повинні розглянути три випадки *, ..?*і .[^.]*відповідно приховані файли, але не .та... Вони не завадять, якщо у нас немає опції nullglob, оскільки вони також ніколи не кажуть, що вона порожня, коли її немає. Отже, остаточне запропоноване рішення:
function emptydir {
[ "$1/"* "" = "" ] 2> /dev/null &&
[ "$1/"..?* "" = "" ] 2> /dev/null &&
[ "$1/".[^.]* "" = "" ] 2> /dev/null ||
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
Охорона безпеки:
Створіть два файли rmта xв порожньому каталозі та виконайте *підказку. Глобус *розшириться до, rm xі це буде виконано для видалення x. Це не є проблемою безпеки, оскільки в нашій функції глобуси розташовані там, де розширення розглядаються не як команди, а як аргументи, як у for f in *.