it-roy-ru.com

Проверка из сценария Shell, если каталог содержит файлы

Как проверить, содержит ли каталог файлы из сценария Shell?

Что-то похожее на это

if [ -e /some/dir/* ]; then echo "huzzah"; fi;

но это работает, если каталог содержит один или несколько файлов (вышеупомянутый работает только с 0 или 1 файлами).

95
ionn

Решения до сих пор используют ls. Вот все решение Bash:

#!/bin/bash
shopt -s nullglob dotglob     # To include hidden files
files=(/some/dir/*)
if [ ${#files[@]} -gt 0 ]; then echo "huzzah"; fi
61
Bruno De Fraine

Три лучшие трюки


shopt -s nullglob dotglob; f=your/dir/*; ((${#f}))

Этот трюк на 100% bash и вызывает (порождает) суб-оболочку. Идея взята из Bruno De Fraine и улучшена комментарием teambob

files=$(shopt -s nullglob dotglob; echo your/dir/*)
if (( ${#files} ))
then
  echo "contains files"
else 
  echo "empty (or does not exist or is a file)"
fi

Примечание: нет разницы между пустым каталогом и несуществующим каталогом (и даже если указанный путь является файлом).

Есть похожая альтернатива и больше деталей (и больше примеров) на 'official' FAQ для канала #bash IRC :

if (shopt -s nullglob dotglob; f=(*); ((${#f[@]})))
then
  echo "contains files"
else 
  echo "empty (or does not exist, or is a file)"
fi

[ -n "$(ls -A your/dir)" ]

Этот трюк вдохновлен статья nixCraft , опубликованным в 2007 году. Добавьте 2>/dev/null для подавления ошибки вывода "No such file or directory".
См. Также ответ { Эндрю Тейлор (2008 г.) и ответ gr8can8dian (2011 г.).

if [ -n "$(ls -A your/dir 2>/dev/null)" ]
then
  echo "contains files (or is a file)"
else
  echo "empty (or does not exist)"
fi

или однострочная версия bashism:

[[ $(ls -A your/dir) ]] && echo "contains files" || echo "empty"

Примечание:ls возвращает $?=2, когда каталог не существует. Но нет разницы между файлом и пустой директорией.


[ -n "$(find your/dir -Prune -empty)" ]

Этот последний трюк вдохновлен ответ gravstar , где -maxdepth 0 заменен на -Prune и улучшен комментарием phils .

if [ -n "$(find your/dir -Prune -empty 2>/dev/null)" ]
then
  echo "empty (directory or file)"
else
  echo "contains files (or does not exist)"
fi

вариант с использованием -type d:

if [ -n "$(find your/dir -Prune -empty -type d 2>/dev/null)" ]
then
  echo "empty directory"
else
  echo "contains files (or does not exist or is not a directory)"
fi

Объяснение: 

  • find -Prune похож на find -maxdepth 0, используя меньше символов
  • find -empty печатает пустые каталоги и файлы
  • find -type d печатает только каталоги

Примечание: Вы также можете заменить [ -n "$(find your/dir -Prune -empty)" ] только сокращенной версией ниже:

if [ `find your/dir -Prune -empty 2>/dev/null` ]
then
  echo "empty (directory or file)"
else
  echo "contains files (or does not exist)"
fi

Этот последний код работает в большинстве случаев, но помните, что вредоносные пути могут выражать команду ...

114
olibre

Как насчет следующего:

if find /some/dir/ -maxdepth 0 -empty | read v; then echo "Empty dir"; fi

Таким образом, нет необходимости генерировать полный список содержимого каталога. read одновременно отбрасывает вывод и заставляет выражение принимать значение true только тогда, когда что-то читается (т. Е. /some/dir/ обнаруживается пустым find).

47
mweerden

Пытаться:

if [ ! -z `ls /some/dir/*` ]; then echo "huzzah"; fi
19
Greg Hewgill
# Works on hidden files, directories and regular files
### isEmpty()
# This function takes one parameter:
# $1 is the directory to check
# Echoes "huzzah" if the directory has files
function isEmpty(){
  if [ "$(ls -A $1)" ]; then
    echo "huzzah"
  else 
    echo "has no files"
  fi
}
14
gr8can8dian

Берегите каталоги с большим количеством файлов! Для оценки команды ls может потребоваться некоторое время.

ИМО лучшее решение, которое использует 

find /some/dir/ -maxdepth 0 -empty
12
Gravstar
DIR="/some/dir"
if [ "$(ls -A $DIR)" ]; then
     echo 'There is something alive in here'
fi
9
Andrew Taylor

Не могли бы вы сравнить результаты этого?

 ls -A /some/dir | wc -l
5
DGM
 # Проверяет, содержит ли каталог какие-либо скрытые файлы .
 # 
 # Using: if isempty "$ HOME"; затем эхо "Добро пожаловать домой"; fi 
 # 
 isempty () {
 для _ief в $ 1/*; делать
 if [-e "$ _ief"]; затем
 вернуть 1 
 фи 
 сделанный
 вернуть 0 
} 

Некоторые замечания по реализации:

  • Цикл for избегает вызова внешнего процесса ls. Он все еще читает все записи каталога один раз. Это можно оптимизировать, только написав программу на C, которая явно использует readdir ().
  • test -e внутри цикла перехватывает случай пустой директории, в этом случае переменной _ief будет присвоено значение "somedir/*". Только если этот файл существует, функция возвратит «непустое»
  • Эта функция будет работать во всех реализациях POSIX. Но имейте в виду, что Solaris/bin/sh не попадает в эту категорию. Его реализация test не поддерживает флаг -e.
4
Roland Illig

Это говорит мне, если каталог пуст или, если это не так, количество файлов, которые он содержит.

directory="/some/dir"
number_of_files=$(ls -A $directory | wc -l)

if [ "$number_of_files" == "0" ]; then
    echo "directory $directory is empty"
else
    echo "directory $directory contains $number_of_files files"
fi
3
Daishi

Это может быть действительно поздний ответ, но вот решение, которое работает. Эта строка распознает только существование файлов! Это не даст вам ложного срабатывания, если каталоги существуют.

if find /path/to/check/* -maxdepth 0 -type f | read
  then echo "Files Exist"
fi
2
bitstreamer
dir_is_empty() {
   [ "${1##*/}" = "*" ]
}

if dir_is_empty /some/dir/* ; then
   echo "huzzah"
fi

Предположим, у вас нет файла с именем * в /any/dir/you/check, он должен работать с bashdashposhbusybox sh и zsh, но (для zsh) требуется unsetopt nomatch.

Производительность должна быть сопоставима с любым параметром ls, использующим * (glob), я полагаю, он будет медленным на каталогах со многими узлами (мой /usr/bin с 3000+ файлами пошел не так медленно), будет использовать как минимум память, достаточную для выделения всех каталогов/имен файлов ( и более) поскольку все они передаются (разрешаются) в функцию в качестве аргументов, некоторые оболочки, вероятно, имеют ограничения на количество аргументов и/или длину аргументов.

Было бы неплохо иметь переносимый быстрый O(1) нулевой ресурс способ проверить, пуст ли каталог.

Обновление

Приведенная выше версия не учитывает скрытые файлы/каталоги, если требуется еще какой-то тест, например, is_empty из трюки Rich's sh (POSIX Shell) :

is_empty () (
cd "$1"
set -- .[!.]* ; test -f "$1" && return 1
set -- ..?* ; test -f "$1" && return 1
set -- * ; test -f "$1" && return 1
return 0 )

Но вместо этого я думаю о чем-то вроде этого:

dir_is_empty() {
    [ "$(find "$1" -name "?*" | dd bs=$((${#1}+3)) count=1 2>/dev/null)" = "$1" ]
}

Некоторая обеспокоенность в отношении конечных косых черт, отличающихся от аргумента и результатов поиска, когда каталог пуст, и конечные строки (но это должно быть легко обрабатывать), к сожалению, в моей busyboxsh показывают, что, вероятно, является ошибкой в ​​канале find -> dd с выводом усекается случайным образом (если я использовал cat, вывод всегда одинаковый, кажется, dd с аргументом count).

1
Alex

ЗШ

Я знаю, что вопрос был отмечен для Баш; но, просто для справки, для zsh users:

Тест для непустого каталога

Чтобы проверить, не является ли foo не пустым:

$ for i in foo(NF) ; do ... ; done

где, если foo не пусто, будет выполнен код в блоке for.

Тест на пустой каталог

Чтобы проверить, является ли foo пустым:

$ for i in foo(N/^F) ; do ... ; done

где, если foo пуст, код в блоке for будет выполнен.

Заметки

Нам не нужно было цитировать каталог foo выше, но мы можем сделать это, если нам нужно:

$ for i in 'some directory!'(NF) ; do ... ; done

Мы также можем протестировать более одного объекта, даже если это не каталог:

$ mkdir X     # empty directory
$ touch f     # regular file
$ for i in X(N/^F) f(N/^F) ; do echo $i ; done  # echo empty directories
X

Все, что не является каталогом, будет просто проигнорировано.

Дополнительно

Поскольку мы используем globbing, мы можем использовать любой glob (или расширение скобки):

$ mkdir X X1 X2 Y Y1 Y2 Z
$ touch Xf                    # create regular file
$ touch X1/f                  # directory X1 is not empty
$ touch Y1/.f                 # directory Y1 is not empty
$ ls -F                       # list all objects
X/ X1/ X2/ Xf Y/ Y1/ Y2/ Z/
$ for i in {X,Y}*(N/^F); do printf "$i "; done; echo  # print empty directories
X X2 Y Y2

Мы также можем исследовать объекты, которые размещены в массиве. С каталогами, как указано выше, например:

$ ls -F                       # list all objects
X/ X1/ X2/ Xf Y/ Y1/ Y2/ Z/
$ arr=(*)                     # place objects into array "arr"
$ for i in ${^arr}(N/^F); do printf "$i "; done; echo
X X2 Y Y2 Z

Таким образом, мы можем тестировать объекты, которые уже могут быть установлены в параметре массива.

Обратите внимание, что код в блоке for, очевидно, выполняется по очереди в каждом каталоге. Если это нежелательно, вы можете просто заполнить параметр массива и затем работать с этим параметром:

$ for i in *(NF) ; do full_directories+=($i) ; done
$ do_something $full_directories

Объяснение

Для пользователей zsh существует спецификатор glob (F) (см. man zshexpn), который соответствует «полным» (непустым) каталогам:

$ mkdir X Y
$ touch Y/.f        # Y is now not empty
$ touch f           # create a regular file
$ ls -dF *          # list everything in the current directory
f X/ Y/
$ ls -dF *(F)       # will list only "full" directories
Y/

В квалификаторе (F) перечислены объекты, которые соответствуют: каталог И не пуст. Итак, (^F) соответствует: не каталог OR пуст. Таким образом, один (^F), например, также будет перечислять обычные файлы. Таким образом, как объяснено на справочной странице zshexp, нам также нужен спецификатор glob (/), в котором перечислены только каталоги:

$ mkdir X Y Z
$ touch X/f Y/.f    # directories X and Y now not empty
$ for i in *(/^F) ; do echo $i ; done
Z

Таким образом, чтобы проверить, является ли данный каталог пустым, вы можете запустить:

$ mkdir X
$ for i in X(/^F) ; do echo $i ; done ; echo "finished"
X
finished

и просто чтобы быть уверенным, что непустой каталог не будет захвачен:

$ mkdir Y
$ touch Y/.f
$ for i in Y(/^F) ; do echo $i ; done ; echo "finished"
zsh: no matches found: Y(/^F)
finished

К сожалению! Поскольку Y не является пустым, zsh не находит совпадений для (/^F) («пустые каталоги») и, таким образом, выдает сообщение об ошибке, в котором говорится, что совпадений для глоба не найдено. Поэтому нам нужно подавить эти возможные сообщения об ошибках с помощью спецификатора glob (N):

$ mkdir Y
$ touch Y/.f
$ for i in Y(N/^F) ; do echo $i ; done ; echo "finished"
finished

Таким образом, для непустых каталогов нам нужен квалификатор (N/^F), который вы можете прочитать как: «не предупреждайте меня о сбоях, каталогах, которые не заполнены».

Точно так же для пустых каталогов нам нужен квалификатор (NF), который мы также можем прочитать следующим образом: «Не предупреждайте меня о сбоях, полные каталоги».

1
Zorawar

Небольшая вариация ответ Бруно :

files=$(ls -1 /some/dir| wc -l)
if [ $files -gt 0 ] 
then
    echo "Contains files"
else
    echo "Empty"
fi

Меня устраивает

1
loockass
if ls /some/dir/* >/dev/null 2>&1 ; then echo "huzzah"; fi;
0
Toby

Я удивлен руководство по пустым каталогам не упоминалось. Это руководство и все, что действительно нужно знать о лесах, необходимо прочитать для вопросов типа «Шелл».

Примечания с этой страницы:

Никогда не пытайтесь анализировать вывод ls. Даже решения ls -A могут сломаться (например, в HP-UX, если вы root, ls -A делает Полная противоположность тому, что он делает, если вы не root - и нет, я не могу придумать то, что невероятно глупо).

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

   # Bourne
   find "$somedir" -type f -exec echo Found unexpected file {} \;
   find "$somedir" -maxdepth 0 -empty -exec echo {} is empty. \;  # GNU/BSD
   find "$somedir" -type d -empty -exec cp /my/configfile {} \;   # GNU/BSD

Чаще всего все, что действительно нужно, это что-то вроде этого:

   # Bourne
   for f in ./*.mpg; do
        test -f "$f" || continue
        mympgviewer "$f"
    done

Другими словами, человек, задающий вопрос, мог подумать, что явный тест пустого каталога был необходимо, чтобы избежать сообщения об ошибке типа mympgviewer: ./*.mpg: такого файла или каталога нет, хотя на самом деле нет такой тест обязателен.

0
bishop

Я бы пошел на find:

if [ -z "$(find $dir -maxdepth 1 -type f)" ]; then
    echo "$dir has NO files"
else
    echo "$dir has files"

Это проверяет вывод поиска только файлов в каталоге, не просматривая подкаталоги. Затем он проверяет вывод, используя опцию -z, взятую из man test:

   -z STRING
          the length of STRING is zero

Смотрите некоторые результаты:

$ mkdir aaa
$ dir="aaa"

Пустой каталог:

$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
empty

Просто реж в нем

$ mkdir aaa/bbb
$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
empty

Файл в каталоге:

$ touch aaa/myfile
$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
$ rm aaa/myfile 

Файл в подкаталоге:

$ touch aaa/bbb/another_file
$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
empty
0
fedorqui

С некоторым обходным путем я мог найти простой способ выяснить, есть ли файлы в каталоге. Это может быть расширено с помощью команд grep для проверки файлов .xml, .txt и т.д. Пример: ls /some/dir | grep xml | wc -l | grep -w "0"

#!/bin/bash
if ([ $(ls /some/dir | wc -l  | grep -w "0") ])
    then
        echo 'No files'
    else
        echo 'Found files'
fi
0
chanaka777

До сих пор я не видел ответа, использующего grep, который, я думаю, дал бы более простой ответ (не слишком много странных символов!). Вот как я бы Проверил, существуют ли какие-либо файлы в каталоге с помощью Bourne Shell:

это возвращает количество файлов в каталоге:

ls -l <directory> | egrep -c "^-"

вы можете указать путь к каталогу, в котором написан каталог. Первая половина канала гарантирует, что первый символ вывода будет «-» для каждого файла. Затем egrep подсчитывает количество строк, начинающихся с этого символа , используя регулярные выражения. Теперь все, что вам нужно сделать, это сохранить полученное число и сравнить его с помощью обратных кавычек: 

 #!/bin/sh 
 fileNum=`ls -l <directory> | egrep -c "^-"`  
 if [ $fileNum == x ] 
 then  
 #do what you want to do
 fi

х - это переменная по вашему выбору.

0
Jecht Tyre

Попробуйте с помощью команды find . Укажите директорию, жестко закодированную или в качестве аргумента . Затем запустите find для поиска по всем файлам внутри директории . Проверьте, равен ли возвращаемый результат поиска null . Выведите данные поиска

#!/bin/bash

_DIR="/home/user/test/"
#_DIR=$1
_FIND=$(find $_DIR -type f )
if [ -n "$_FIND" ]
then
   echo -e "$_DIR contains files or subdirs with files \n\n "
   echo "$_FIND"
else
echo "empty (or does not exist)"
fi
0
GiannakopoulosJ

Простой ответ с bash :

if [[ $(ls /some/dir/) ]]; then echo "huzzah"; fi;
0
Thomas Steinbach

Смешивая чернослив вещи и последние ответы, я получил

find "$some_dir" -Prune -empty -type d | read && echo empty || echo "not empty"

это работает для путей с пробелами тоже

0
Laurent G