Bash scripting: перевірка порожнього каталогу


95

Я хочу перевірити, чи каталог не містить файлів. Якщо так, я пропущу деяку обробку.

Я спробував таке:

if [ ./* == "./*" ]; then
    echo "No new file"
    exit 1
fi

Це дає таку помилку:

line 1: [: too many arguments

Чи є рішення / альтернатива?


Відповіді:


121
if [ -z "$(ls -A /path/to/dir)" ]; then
   echo "Empty"
else
   echo "Not Empty"
fi

Також було б круто перевірити, чи існує каталог раніше.


11
Не використовуйте &&і ||одночасно! Якщо echo "Not Empty"не вдасться, echo "Empty"побіжить! Спробуйте echo "test" && false || echo "fail"! Так, я знаю echo, що не вийде з ладу, але якщо зміниш будь-яку іншу команду, ти здивуєшся!
uzsolt

4
Надайте, принаймні, один приклад, коли наведений вище код не працює. Конкретно цей код абсолютно правильний. Я сподіваюся, що
аскер

3
[ "$(ls -A /)" ] && touch /non-empty || echo "Empty"- якщо ви хочете "позначити" непорожні dirs створеним файлом з назвою non-empty, вийде з ладу і надрукує "Empty".
uzsolt

4
де touch /emptyв моїй лінії?
Андрій Атапін

7
О ні! У цьому коді є дуже важлива проблема. Це повинно бути if [ -n "$(ls -A /path/to/dir)" ]; then... Будь ласка, оновіть відповідь, перш ніж хтось десь вставить цей код на свій сервер, і хакер з’ясує, як його використовувати. Якщо /path/to/dirце не порожньо, то імена файлів передаються як аргументи, до /bin/testяких явно не призначено. Просто додайте -nаргумент, вирішена проблема. Дякую!
Едвард Нед Харві

21

Не потрібно рахувати нічого або кульових кульок. Ви також можете використовувати readв поєднанні з find. Якщо findвихідний результат порожній, ви повернетесь false:

if find /some/dir -mindepth 1 | read; then
   echo "dir not empty"
else
   echo "dir empty"
fi

Це повинно бути портативним.


Гарне рішення, але я думаю, що ваші echoдзвінки відображають неправильний результат: у моєму тесті (під Цигвіном) find . -mindepth 1 | readбув код помилки 141 у не порожньому режимі, а 0 - у порожньому режимі
Лукас Цимон

@LucasCimon Не тут (macOS та GNU / Linux). Для непорожньої каталогу, readповертає 0, а для порожнього 1.
slhck

1
PSA не працюєset -o pipefail
полковник тридцять два

19
if [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; then
    echo "Empty directory"
else
    echo "Not empty or NOT a directory"
fi

Правильно і швидко. Приємно!
l0b0

4
Для цього потрібні лапки (2х) і тест -nмає бути правильним і безпечним (тестуйте з каталогом з пробілами в імені, протестуйте його з не порожнім каталогом з назвою '0 = 1'). ... [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; ...
Зрін

1
@ivan_pozdeev Це неправда, принаймні для GNU знайти. Ви можете думати про це grep. serverfault.com/questions/225798/…
Володимир Пантелеев

Це може бути простіше писати find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty | grep .і покладатися на статус виходу з grep. Яким би способом ви це не зробили, це дуже правильна відповідь на це питання.
Том Андерсон

13
#!/bin/bash
if [ -d /path/to/dir ]; then
    # the directory exists
    [ "$(ls -A /path/to/dir)" ] && echo "Not Empty" || echo "Empty"
else
    # You could check here if /path/to/dir is a file with [ -f /path/to/dir]
fi

4
Це повинно бути, немає необхідності розбирати ls вихід, просто подивіться, чи він порожній чи ні. Використання знаходження просто відчуває себе зайвим для мене.
акостадінов

4

Це зробить роботу в поточному робочому каталозі (.):

[ `ls -1A . | wc -l` -eq 0 ] && echo "Current dir is empty." || echo "Current dir has files (or hidden files) in it."

або одна і та ж команда розділена на три рядки просто для того, щоб бути читабельнішою:

[ `ls -1A . | wc -l` -eq 0 ] && \
echo "Current dir is empty." || \
echo "Current dir has files (or hidden files) in it."

Просто замініть ls -1A . | wc -lз , ls -1A <target-directory> | wc -lякщо вам потрібно , щоб запустити його на інший цільовий папці.

Edit : я замінив -1aз -1A(див @Daniel коментар)


2
використовувати ls -Aзамість цього. У деяких файлових системах немає .і ..символічних посилань згідно з документами.
Даніель Бек

1
Дякую @Daniel, я змінив свою відповідь після вашої пропозиції. Я знаю, що "1" також може бути видалено.
ztank1013

2
Це не шкодить, але мається на увазі, якщо вивести його не в термінал. Оскільки ви передаєте його в іншу програму, це зайве.
Даніель Бек

-1безумовно зайве. Навіть якщо lsне буде друкувати один елемент на рядок, коли він буде прокладений, то це не вплине на ідею перевірити, чи він дав нуль чи більше рядків.
Віктор Ярема

3

Використовуйте наступне:

count="$( find /path -mindepth 1 -maxdepth 1 | wc -l )"
if [ $count -eq 0 ] ; then
   echo "No new file"
   exit 1
fi

Таким чином, ви не залежать від вихідного формату ls. -mindepthпропускає сам каталог, -maxdepthне дозволяє рекурсивно захищатись у підкаталогах, щоб прискорити роботу.


Звичайно, тепер ви залежите від формату wc -lта findвиводу (що досить зрозуміло).
Даніель Бек

3

Використання масиву:

files=( * .* )
if (( ${#files[@]} == 2 )); then
    # contents of files array is (. ..)
    echo dir is empty
fi

3
Дуже приємне рішення, але зауважте, що воно вимагаєshopt -s nullglob
xebeche

3
${#files[@]} == 2Припущення не витримує для кореневого каталогу (ви, ймовірно , не перевірити , якщо він порожній , але деякий код , який не знає про те , що обмеження могли б).
ivan_pozdeev

3

Нерозважливий, але єдиний, лише PID-спосіб:

is_empty() {
    test -e "$1/"* 2>/dev/null
    case $? in
        1)   return 0 ;;
        *)   return 1 ;;
    esac
}

Це використовує той факт, що testвбудований виходить з 2, якщо задано більше одного аргументу після -e: По-перше, "$1"/*глобус розширюється bash. Це призводить до одного аргументу на файл. Тому

  • Якщо файлів немає, зірочка в програмі test -e "$1"*не розширюється, тому Shell повертається до спробного імені *, який повертає 1.

  • ... за винятком випадків, коли насправді є один файл, названий точно *, тоді зірочка розгортається до ну, зірочка, яка закінчується тим же викликом, що і вище, тобто. test -e "dir/*", якраз цей час повертає 0. (Дякую @TrueY за вказівку на це.)

  • Якщо є один файл, test -e "dir/file"запускається, який повертає 0.

  • Але якщо файлів більше, ніж 1, test -e "dir/file1" "dir/file2" запускається, що bash повідомляє про це як про помилку використання, тобто 2.

case обертає всю логіку навколо, так що лише перший випадок із 1 статусом виходу повідомляється про успіх.

Можливі проблеми, які я не перевірив:

  • Файлів більше, ніж кількість дозволених аргументів - я думаю, що це може поводитись так, як у випадку з файлами 2+.

  • Або насправді є файл із порожнім ім’ям - я не впевнений, що це можливо в будь-якій здоровій ОС / FS.


1
Незначна корекція: якщо у dir / файлі немає файлу, тоді test -e dir/*він викликається. Якщо єдиним файлом є "*" у dir, тест поверне 0. Якщо файлів більше, то він повертається 2. Отже, він працює як описано.
TrueY

Ви маєте рацію, @TrueY, я включив це у відповідь. Дякую!
Алоїз Магдал

2

За допомогою FIND (1) (під Linux та FreeBSD) ви можете нерекурсивно дивитись на запис каталогу через "-maxdepth 0" і перевіряти, чи порожній він з "-empty". Застосовується до питання, яке дає:

if test -n "$(find ./ -maxdepth 0 -empty)" ; then
    echo "No new file"
    exit 1
fi

Це може бути не 100% портативним, але елегантним.
Крейг Рінгер

1

Я думаю, що найкраще рішення:

files=$(shopt -s nullglob; shopt -s dotglob; echo /MYPATH/*)
[[ "$files" ]] || echo "dir empty" 

завдяки https://stackoverflow.com/a/91558/520567

Це анонімне редагування моєї відповіді, яке комусь може бути або не корисно: незначна зміна дає кількість файлів:

files=$(shopt -s nullglob dotglob; s=(MYPATH/*); echo ${s[*]}) 
echo "MYPATH contains $files files"

Це буде працювати правильно, навіть якщо файли містять пробіли.


1
if find "${DIR}" -prune ! -empty -exit 1; then
    echo Empty
else
    echo Not Empty
fi

EDIT: Я думаю, що це рішення добре працює з gnu find, після швидкого перегляду реалізації . Але це може не спрацювати, наприклад, з знахідкою netbsd . Дійсно, що використовується поле st_size поля stat (2). Посібник описує це як:

st_size            The size of the file in bytes.  The meaning of the size
                   reported for a directory is file system dependent.
                   Some file systems (e.g. FFS) return the total size used
                   for the directory metadata, possibly including free
                   slots; others (notably ZFS) return the number of
                   entries in the directory.  Some may also return other
                   things or always report zero.

Кращим рішенням, також простішим, є:

if find "${DIR}" -mindepth 1 -exit 1; then
    echo Empty
else
    echo Not Empty
fi

Крім того, -прун в 1-му розчині марний.

EDIT: немає -exit для gnu find .. рішення вище добре для пошуку NetBSD. Для пошуку GNU це має працювати:

if [ -z "`find \"${DIR}\" -mindepth 1 -exec echo notempty \; -quit`" ]; then
    echo Empty
else
    echo Not Empty
fi

знайти з GNU findutils 4.6.0 (остання версія) не має -exitприсудка .
Денніс

0

Це все чудові речі - я просто перетворив його на сценарій, щоб я міг перевірити наявність порожніх каталогів нижче поточного. Нижче слід помістити файл, який називається "findempty", помістити в шлях десь, щоб bash міг його знайти, а потім chmod 755 запустити. Думаю, можна легко змінити ваші конкретні потреби.

#!/bin/bash
if [ "$#" == "0" ]; then 
find . -maxdepth 1 -type d -exec findempty "{}"  \;
exit
fi

COUNT=`ls -1A "$*" | wc -l`
if [ "$COUNT" == "0" ]; then 
echo "$* : $COUNT"
fi

0

Ця робота для мене, щоб перевірити та обробити файли в каталозі ../IN, враховуючи, що скрипт знаходиться в ../Scriptкаталозі:

FileTotalCount=0

    for file in ../IN/*; do
    FileTotalCount=`expr $FileTotalCount + 1`
done

if test "$file" = "../IN/*"
then

    echo "EXITING: NO files available for processing in ../IN directory. "
    exit

else

  echo "Starting Process: Found ""$FileTotalCount"" files in ../IN directory for processing."

# Rest of the Code

0

Як щодо тестування, якщо каталог існує, а не порожній в одному операторі if

if [[ -d path/to/dir && -n "$(ls -A path/to/dir)" ]]; then 
  echo "directory exists"
else
  echo "directory doesn't exist"
fi

-1

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

Не використовуйте цей злом, якщо існують інші процеси, які можуть стати нерозбірливими у каталозі, про який вони знають, на короткий час перестаючи існувати.

Якщо rmdirдля вас не вдасться, і ви можете тестувати каталоги, які потенційно можуть містити велику кількість файлів, будь-яке рішення, що покладається на глобальну оболонку, може повільно та / або запустити обмеження довжини командного рядка. Напевно, краще використовувати findв такому випадку. Найшвидше findрішення, про яке я можу придумати, іде так

is_empty() {
    test -z $(find "$1" -mindepth 1 -printf X -quit)
}

Це працює для версій GNU та BSD, findале не для Solaris, якої не вистачає кожному з цих findоператорів. Любіть свою роботу, Oracle.


Не гарна ідея. ОП просто хотів перевірити, чи каталог був порожнім чи ні.
roaima

-3

Ви можете спробувати видалити каталог і дочекатися помилки; rmdir не видалить каталог, якщо він не порожній.

_path="some/path"
if rmdir $_path >/dev/null 2>&1; then
   mkdir $_path        # create it again
   echo "Empty"
else
   echo "Not empty or doesn't exist"
fi

3
-1 Це такий тип коду, який справляє зворотну реакцію. rmdirне вдасться, якщо я не маю дозволу на видалення каталогу; або якщо це підпункт Btrfs; або якщо він належить до файлової системи лише для читання. А якщо rmdirне виходить з ладу і mkdirзапускається: що робити, якщо вже видалений каталог належав іншому користувачеві? як щодо його (можливо, нестандартних) дозволів? ACL? розширені атрибути? Всі втрачені.
Каміль Маціоровський

Ну, я просто вивчаю баш, і я подумав, що це може бути швидший спосіб, а не повторення через весь каталог, але процесори потужні, і ви праві, а не безпечні.
impxd
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.