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.