как проверить каталог пуст

15

У меня есть требование, если я выполняю скрипт ./123с аргументами пустого пути, скажем /usr/share/linux-headers-3.16.0-34-generic/.tmp_versions(этот каталог пуст). Должен отображаться «каталог пуст»

Мой код:

#!/bin/bash
dir="$1"

if [ $# -ne 1 ]
then
    echo "please pass arguments" 
exit
fi

if [ -e $dir ]
then
printf "minimum file size: %s\n\t%s\n" \
 $(du $dir -hab | sort -n -r | tail -1)

printf "maximum file size: %s\n\t%s\n" \
 $(du $dir -ab | sort -n | tail -1)

printf "average file size: %s"
du $dir -sk | awk '{s+=$1}END{print s/NR}'
else
   echo " directory doesn't exists"
fi

if [ -d "ls -A $dir" ]
 then
    echo " directory is  empty"
fi

У меня появляется сообщение об ошибке, например, если я выполняю имя скрипта ./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions(этот каталог пуст).

minimum file size: 4096
    /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions
maximum file size: 4096
    /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions
average file size: 4

вместо вывода только «директория пуста» показывает вывод выше

Приведенный ниже вывод должен отображаться, если я превышаю сценарий с правильными аргументами (я имею в виду с правильным путем к каталогу). сказать./123 /usr/share

minimum file size: 196
        /usr/share
    maximum file size: 14096
        /usr/share
    average file size: 4000

мой ожидаемый результат: ./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions

directory is empty.
будда срикант
источник
4
См. Mywiki.wooledge.org/BashFAQ/004
Валентин Байрами,
Может кто-нибудь, пожалуйста, проверьте мой код один раз. Я редактировал его
Будда Срикант
Похожие
страницы

Ответы:

20
if    ls -1qA ./somedir/ | grep -q .
then  ! echo somedir is not empty
else  echo somedir is empty
fi

Выше приведен тест POSIX-совместимый, и он должен быть очень быстрым. lsбудут перечислены все файлы / каталоги в директории исключая .и ..каждый из них в каждой строке и -qЦитата все непечатаемые символы (в том числе \newlines) на выходе с ?вопросительным знаком. Таким образом, если grepполучит хотя бы один символ на входе, он вернет true, иначе false.

Чтобы сделать это только в POSIX-оболочке:

cd  ./somedir/ || exit
set ./* ./.[!.]* ./..?*
if   [ -n "$4" ] ||
     for e do 
         [ -L "$e" ] ||
         [ -e "$e" ] && break
     done
then ! echo somedir is not empty
else   echo somedir is empty
fi
cd "$OLDPWD"

POSIX-оболочка (которая не ранее отключило -fпоколение ilename) будет массив позиционно-параметр либо буквальной строке с последующей командой выше, либо к полям , порожденные операторами Глобы в конце каждого из них. Будет ли это так, зависит от того, соответствуют ли глобусы чему-либо. В некоторых оболочках вы можете указать неразрешимому глобу расширяться до нуля - или вообще ничего. Иногда это может быть полезно, но оно не переносимо и часто сопряжено с дополнительными проблемами, такими как необходимость установки специальных параметров оболочки и их последующего сброса.set"$@"set

Единственным переносимым средством обработки аргументов с нулевыми значениями являются пустые или неустановленные переменные или ~расширения тильды. И последнее, кстати, намного безопаснее, чем первое.

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

В любом случае это должно включать только один системный stat()вызов - оболочку, и lsоба должны только stat()запрашивать каталог и перечислять файлы, содержащиеся в отчете его dentries . Это контрастирует с поведением, findкоторое вместо stat()каждого файла вы можете перечислить с ним.

mikeserv
источник
Могу подтвердить. Я использую первоначальный пример mikeserv в написанных мной скриптах.
Отей
Это будет означать пустые каталоги, которые не существуют или для которых у вас нет прав на чтение. Во-вторых, если у вас есть права на чтение, но нет поиска, YMMV.
Стефан Шазелас
@ StéphaneChazelas - возможно, но такие сообщения будут сопровождаться сообщениями об ошибках, чтобы уведомить пользователя об этом.
mikeserv
1
Только по lsделу. и [молчат, когда у них нет доступа. Например, [ -e /var/spool/cron/crontabs/stephane ]будет молча сообщать о ложном, пока есть файл с таким именем.
Стефан Шазелас
Также обратите внимание, что эти решения не эквивалентны, если somedir является символической ссылкой на каталог (или не каталог).
Стефан Шазелас
6

С GNU или современными BSD findвы можете делать:

if find -- "$dir" -prune -type d -empty | grep -q .; then
  printf '%s\n' "$dir is an empty directory"
else
  printf >&2 '%s\n' "$dir is not empty, or is not a directory" \
                    "or is not readable or searchable in which case" \
                    "you should have seen an error message from find above."
fi

(предполагается , что $dirне выглядит как findпредикат ( !, (, -name...).

POSIXly:

if [ -d "$dir" ] && files=$(ls -qAL -- "$dir") && [ -z "$files" ]; then
  printf '%s\n' "$dir is an empty directory"
else
  printf >&2 '%s\n' "$dir is not empty, or is not a directory" \
                    "or is not readable or searchable in which case" \
                    "you should have seen an error message from ls above."
fi
Стефан Шазелас
источник
4

[-z $dir ]жалуется, что в [-zбольшинстве систем не вызывается команда . Вам нужны пробелы вокруг скобок .

[ -z $dir ]бывает true, если dirпусто, и false для большинства других значений dir, но это ненадежно, например, это правда, если значение diris = -zили -o -o -n -n. Всегда используйте двойные кавычки вокруг подстановок команд (это относится и к остальной части вашего скрипта).

[ -z "$dir" ]проверяет, является ли значение переменной dirпустым Значением переменной является строка, которая является путем к каталогу. Это ничего не говорит вам о самом каталоге.

Нет оператора для проверки того, является ли каталог пустым, как для обычного файла ( [ -s "$dir" ]верно для каталога, даже если он пуст). Простой способ проверить, является ли каталог пустым, состоит в перечислении его содержимого; если вы получите пустой текст, каталог будет пустым.

if [ -z "$(ls -A -- "$dir")" ]; then 

На старых системах, которые не имеют ls -A, вы можете использовать ls -a, но тогда .и ..перечислены.

if [ -z "$(LC_ALL=C ls -a -- "$dir")" = ".
.." ]; then 

(Не делайте отступ строки, начинающейся с, ..поскольку строка должна содержать только новую строку, а не новую и лишний пробел.)

Жиль "ТАК - перестань быть злым"
источник
Не могли бы вы объяснить эту часть "$ (ls -A -" $ dir ")". что делает $ внутри и снаружи скобок?
Будда Срикант
@buddhasreekanth $(…)- командная замена
Жиль "
@Giles "$ (ls -A -" $ dir ")" не работает из-за ошибки.
Будда Срикант
@buddhasreekanth В чем ошибка? Копировать вставить. Вы не можете ожидать, что люди помогут вам, если вы просто скажете «не работает», не объяснив, что именно вы наблюдаете.
Жиль "ТАК - перестань быть злым"
@ Джайлз, это ошибка. Когда я выполнял свой сценарий ./filestats, возникала его ошибка .. $ / filestats / home / sreekanth / testdir / aki / schatz минимальный размер файла: 4096 / home / sreekanth / testdir / aki / schatz максимальный размер файла: 4096 / home / sreekanth / testdir / aki / schatz средний размер файла: 4 каталог пуст. Вместо показа "каталог пуст". Его отображение с минимальным размером, максимальным размером и средним размером.
Будда Срикант
3

Вы смотрите на атрибуты самого каталога.

$ mkdir /tmp/foo
$ ls -ld /tmp/foo
drwxr-xr-x 2 jackman jackman 4096 May  8 11:32 /tmp/foo
# ...........................^^^^

Вы хотите посчитать, сколько файлов там:

$ dir=/tmp/foo
$ shopt -s nullglob
$ files=( "$dir"/* "$dir"/.* )
$ echo ${#files[@]}
2
$ printf "%s\n" "${files[@]}"
/tmp/foo/.
/tmp/foo/..

Итак, тест для «каталога пуст»:

function is_empty {
    local dir="$1"
    shopt -s nullglob
    local files=( "$dir"/* "$dir"/.* )
    [[ ${#files[@]} -eq 2 ]]
}

Как это:

$ if is_empty /tmp/foo; then echo "it's empty"; else echo "not empty"; fi
it's empty
$ touch /tmp/foo/afile
$ if is_empty /tmp/foo; then echo "it's empty"; else echo "not empty"; fi
not empty
Гленн Джекман
источник
Это сообщит, что каталог пуст, если у вас нет доступа для чтения к нему или он не существует.
Стефан Шазелас
1

Мой ответ 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 и, что не имеет большого значения, обычно быстрее, чем решение, которое выводит каталог и передает вывод в grep.

Использование:

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 . ; }
}

Он перечисляет каталог и передает результат в grep. Вместо этого я предлагаю простую функцию, основанную на расширении и сравнении глобусов.

function emptydir {   
 [ "$(shopt -s nullglob; echo "$1"/{,.[^.],..?}*)" = "" ]
}

Эта функция не является стандартной POSIX и вызывает подоболочку с $(). Сначала я объясню эту простую функцию, чтобы мы могли лучше понять окончательное решение (см. Ответ tldr выше) позже.

Объяснение:

Левая сторона (LHS) пуста, когда не происходит расширения, что является случаем, когда каталог пуст. Опция nullglob обязательна, потому что в противном случае, когда нет совпадения, сам шар является результатом расширения. (Наличие RHS, совпадающего с глобусами LHS, когда каталог пуст, не работает из-за ложных срабатываний, возникающих, когда глобус LHS соответствует одному файлу с именем самого глобуса: *глобус соответствует подстроке *в имени файла. ) Выражение скобки {,.[^.],..?}охватывает скрытые файлы, но не ..или ..

Поскольку shopt -s nullglobвыполняется внутри $()(подоболочка), это не меняет nullglobопцию текущей оболочки, что обычно хорошо. С другой стороны, это хорошая идея, чтобы установить эту опцию в сценариях, потому что склонен к ошибке, когда глобус возвращает что-то, когда нет совпадения. Таким образом, можно установить опцию nullglob в начале скрипта, и она не понадобится в функции. Давайте помнить об этом: нам нужно решение, которое работает с опцией nullglob.

Предостережения:

Если у нас нет доступа для чтения к каталогу, функция выдает сообщение так же, как если бы был пустой каталог. Это относится также к функции, которая перечисляет каталог и grep выводит.

Команда shopt -s nullglobне стандартная POSIX.

Он использует подоболочку, созданную $(). Это не имеет большого значения, но хорошо, если мы можем избежать этого.

Pro:

Не то чтобы это действительно имеет значение, но эта функция в четыре раза быстрее, чем предыдущая, измеряемая количеством процессорного времени, проведенного в ядре внутри процесса.

Другие решения:

Мы можем удалить ноны 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 *.

Dominic108
источник
$(set -f; echo "$1"/*)кажется довольно сложным способом написания "$1/*". И это не будет соответствовать скрытым каталогам или файлам.
Муру
Я имел в виду, что в файле нет расширения имени файла "$1/*"(обратите внимание, что в кавычках *), поэтому для него set -fне нужны , в частности, и subshell .
Муру
Он отвечает на другой вопрос: содержит ли каталог не скрытые файлы или каталоги. Мне жаль насчет того. Я буду счастлив удалить это.
Dominic108
1
Есть способы , чтобы соответствовать скрытые файлы тоже (см осложненных Glob в ответ mikeserv в : ./* ./.[!.]* ./..?*). Может быть, вы можете включить это, чтобы сделать функцию завершенной.
Муру
Ах! Я посмотрю на это и узнаю, и, возможно, у нас будет полный ответ, основанный на глобальном расширении!
Dominic108
0

Вот еще один простой способ сделать это. Предположим, что D - это полный путь к каталогу, который вы хотите проверить на пустоту.

потом

if [[ $( du -s  D |awk ' {print $1}') = 0 ]]
then
      echo D is empty
fi

Это работает, потому что

du -s D

имеет в качестве выхода

size_of_D   D

awk удаляет D и если размер равен 0, каталог пуст.

Ричард Гостанян
источник
-1
#/bin/sh

somedir=somedir
list="`echo "$somedir"/*`"

test "$list" = "$somedir/*" && echo "$somedir is empty"

# dot prefixed entries would be skipped though, you have to explicitly check them
user137815
источник
2
Привет и добро пожаловать на сайт. Пожалуйста, не размещайте ответы, которые являются просто кодом и без объяснения причин. Прокомментируйте код или иным образом объясните, что вы делаете. Кстати, нет смысла использовать echo, все, что вам нужно, это list="$somedir/*". Кроме того, это сложно и подвержено ошибкам. Вы не упомянули, что ОП должен указать путь к целевому каталогу, включая, например, косую черту.
Terdon