التحقق من البرنامج النصي Shell إذا كان الدليل يحتوي على ملفات

StackOverflow https://stackoverflow.com/questions/91368

  •  01-07-2019
  •  | 
  •  

سؤال

من برنامج شل، كيف يمكنني التحقق مما إذا كان الدليل يحتوي على ملفات؟

شيء مشابه لهذا

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

ولكنه يعمل إذا كان الدليل يحتوي على ملف واحد أو عدة ملفات (الملف أعلاه يعمل فقط مع 0 أو 1 ملف بالضبط).

هل كانت مفيدة؟

المحلول

الحلول المستخدمة حتى الآن ls.إليك حل bash بالكامل:

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

نصائح أخرى

أفضل ثلاث حيل


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

هذه الخدعة 100% bash ويستدعي (يولد) قذيفة فرعية.الفكرة من برونو دي فرين وتحسينها com.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

ملحوظة: لا يوجد فرق بين الدليل الفارغ والدليل غير الموجود (وحتى عندما يكون المسار المقدم ملفًا).

يوجد بديل مشابه ومزيد من التفاصيل (والمزيد من الأمثلة) على 'رسمي' الأسئلة الشائعة لقناة #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

أو نسخة الباشيّة ذات السطر الواحد:

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

ملحوظة: ls عائدات $?=2 عندما لا يكون الدليل موجودا.ولكن لا يوجد فرق بين الملف والدليل الفارغ.


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

هذه الحيلة الأخيرة مستوحاة من إجابة جرافستار أين -maxdepth 0 لقد بدل بواسطة -prune وتحسينها فيلستعليق.

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

يعمل هذا الرمز الأخير في معظم الحالات ولكن انتبه إلى أن المسارات الضارة قد تعبر عن أمر ما...

ماذا عن ما يلي:

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

بهذه الطريقة ليست هناك حاجة لإنشاء قائمة كاملة بمحتويات الدليل.ال read هو تجاهل المخرجات وجعل التعبير يتم تقييمه على أنه صحيح فقط عند قراءة شيء ما (على سبيل المثال. /some/dir/ تم العثور عليها فارغة بواسطة find).

يحاول:

if [ ! -z `ls /some/dir/*` ]; then echo "huzzah"; fi
# 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
}

اعتني بالدلائل التي تحتوي على الكثير من الملفات!قد يستغرق الأمر بعض الوقت لتقييم ls يأمر.

IMO الحل الأفضل هو الذي يستخدم

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

هل يمكنك مقارنة مخرجات هذا؟

 ls -A /some/dir | wc -l
# Checks whether a directory contains any nonhidden files.
#
# usage: if isempty "$HOME"; then echo "Welcome home"; fi
#
isempty() {
    for _ief in $1/*; do
        if [ -e "$_ief" ]; then
            return 1
        fi
    done
    return 0
}

بعض ملاحظات التنفيذ:

  • ال for حلقة تتجنب الاتصال بمصدر خارجي ls عملية.فإنه لا يزال يقرأ كافة إدخالات الدليل مرة واحدة.لا يمكن تحسين ذلك إلا عن طريق كتابة برنامج C يستخدم readdir() بشكل صريح.
  • ال test -e داخل الحلقة يلتقط حالة الدليل الفارغ، وفي هذه الحالة المتغير _ief سيتم تعيين القيمة "somedir/*".فقط في حالة وجود هذا الملف، ستعيد الدالة "غير فارغ"
  • ستعمل هذه الوظيفة في جميع تطبيقات POSIX.لكن انتبه إلى أن Solaris /bin/sh لا يندرج ضمن هذه الفئة.إنه test التنفيذ لا يدعم -e علَم.

يخبرني هذا إذا كان الدليل فارغًا أو إذا لم يكن كذلك، يخبرني بعدد الملفات التي يحتوي عليها.

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

قد يكون هذا ردًا متأخرًا حقًا ولكن إليك الحل الناجح.هذا الخط يتعرف فقط على وجود الملفات!لن يعطيك نتيجة إيجابية كاذبة في حالة وجود أدلة.

if find /path/to/check/* -maxdepth 0 -type f | read
  then echo "Files Exist"
fi

أنا مندهش دليل Wooledge على الدلائل الفارغة لم يتم ذكرها.هذا الدليل، وكل كتاب Wooledge حقًا، يجب قراءته للأسئلة المتعلقة بنوع الصدفة.

ملاحظة من تلك الصفحة:

لا تحاول أبدًا تحليل إخراج ls.حتى حلول ls -A يمكن أن تنكسر (على سبيل المثال.على HP -UX ، إذا كنت جذرًا ، فإن ls -a تقوم بالعكس تمامًا لما يفعله إذا لم تكن جذرًا -ولا ، لا يمكنني تكوين شيء غبي بشكل لا يصدق).

في الواقع، قد يرغب المرء في تجنب السؤال المباشر تمامًا.عادة ما يريدون الناس معرفة ما إذا كان الدليل فارغًا لأنهم يريدون فعل شيء ينطوي على الملفات فيه ، إلخ.انظر إلى السؤال الأكبر.على سبيل المثال، قد يكون أحد هذه الأمثلة القائمة على البحث حلاً مناسبًا:

   # 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:./*.ميلا في الغالون:لا يوجد مثل هذا الملف أو الدليل في الواقع لا يوجد مثل هذا الاختبار مطلوب.

dir_is_empty() {
   [ "${1##*/}" = "*" ]
}

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

افترض أنه ليس لديك ملف اسمه * داخل /any/dir/you/check, ، ينبغي أن تعمل على bash dash posh busybox sh و zsh ولكن (لZSH) تتطلب unsetopt nomatch.

يجب أن تكون العروض قابلة للمقارنة بأي منها ls التي تستخدم *(glob)، أعتقد أنه سيكون بطيئًا في الدلائل التي تحتوي على العديد من العقد (my /usr/bin مع أكثر من 3000 ملف لم يكن بطيئًا)، سيستخدم على الأقل ذاكرة كافية لتخصيص جميع dirs/أسماء الملفات (وأكثر) حيث يتم تمريرها جميعًا (حلها) إلى الوظيفة كوسيطات، ربما يكون لدى بعض الأصداف حدود على عدد الوسائط و / أو طول الوسائط.

سيكون من الجيد الحصول على طريقة محمولة سريعة O(1) صفرية للموارد للتحقق مما إذا كان الدليل فارغًا.

تحديث

الإصدار أعلاه لا يأخذ في الاعتبار الملفات/الأديرة المخفية، في حالة الحاجة إلى المزيد من الاختبارات، مثل is_empty من حيل ريتش (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" ]
}

بعض القلق بشأن اختلافات الخطوط المائلة اللاحقة عن الوسيطة ومخرجات البحث عندما يكون dir فارغًا، وأسطرًا جديدة زائدة (ولكن يجب أن يكون من السهل التعامل مع هذا)، للأسف على بلدي busybox sh أظهر ما هو على الأرجح خطأ في find -> dd تم اقتطاع الأنبوب مع الإخراج بشكل عشوائي (إذا استخدمت cat يبدو أن الإخراج هو نفسه دائمًا dd مع الحجة count).

اختلاف صغير من الجواب برونو:

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

إنه يعمل بالنسبة لي

زش

أعلم أن السؤال تم تحديده لـ bash؛ولكن، للإشارة فقط، ل zsh المستخدمين:

اختبار الدليل غير الفارغ

للتحقق مما إذا 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

سيتم تجاهل أي شيء ليس دليلاً.

إضافات

نظرًا لأننا نتحرك، يمكننا استخدام أي كرة (أو توسيع قوس):

$ 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 هناك (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) اعواد الكبريت:ليس دليلاً أو أنه فارغ.هكذا، (^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) ("الدلائل الفارغة") وبالتالي تظهر رسالة خطأ تفيد بأنه لم يتم العثور على أي تطابقات للكرة الأرضية.لذلك نحتاج إلى منع رسائل الخطأ المحتملة هذه باستخدام ملف (N) تصفيات الكرة الأرضية:

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

وبالتالي، بالنسبة للأدلة غير الفارغة، نحتاج إلى المؤهل (N/^F), ، والتي يمكنك قراءتها على النحو التالي:"لا تحذرني من حالات الفشل، فالدلائل غير ممتلئة".

وبالمثل، بالنسبة للأدلة الفارغة، نحتاج إلى المؤهل (NF), ، والتي يمكننا قراءتها أيضًا على النحو التالي:"لا تحذرني من حالات الفشل، الأدلة الكاملة".

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

لم أر حتى الآن إجابة تستخدم 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

x هو متغير من اختيارك.

يجب أن أقوم بالخلط بين الأشياء المقشورة والإجابات الأخيرة

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

يعمل مع المسارات ذات المسافات أيضًا

إجابة بسيطة مع سحق:

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

سأذهب ل 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

فقط dirs في ذلك:

$ 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

حاول باستخدام الأمر find.حدد الدليل المضمن أو كوسيطة.ثم ابدأ البحث للبحث في جميع الملفات داخل الدليل.تحقق مما إذا كانت إرجاع البحث فارغًا.صدى بيانات البحث

#!/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

مع بعض الحلول البديلة، يمكنني العثور على طريقة بسيطة لمعرفة ما إذا كانت هناك ملفات في الدليل.يمكن أن يمتد هذا إلى المزيد باستخدام أوامر 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

أنا لا أحب ls - A الحلول المنشورة.على الأرجح أنك ترغب في اختبار ما إذا كان الدليل فارغًا لأنك لا ترغب في حذفه.ما يلي يفعل ذلك.ومع ذلك، إذا كنت ترغب فقط في تسجيل ملف فارغ، فمن المؤكد أن حذفه وإعادة إنشائه سيكون أسرع من إدراج ملفات لا حصر لها؟

هذا يجب أن يعمل...

if !  rmdir ${target}
then
    echo "not empty"
else
    echo "empty"
    mkdir ${target}
fi

لاختبار دليل هدف محدد

if [ -d $target_dir ]; then
    ls_contents=$(ls -1 $target_dir | xargs); 
    if [ ! -z "$ls_contents" -a "$ls_contents" != "" ]; then
        echo "is not empty";
    else
        echo "is empty";
    fi;
else
    echo "directory does not exist";
fi;

يعمل بشكل جيد بالنسبة لي (عند وجود دير):

some_dir="/some/dir with whitespace & other characters/"
if find "`echo "$some_dir"`" -maxdepth 0 -empty | read v; then echo "Empty dir"; fi

مع الفحص الكامل:

if [ -d "$some_dir" ]; then
  if find "`echo "$some_dir"`" -maxdepth 0 -empty | read v; then echo "Empty dir"; else "Dir is NOT empty" fi
fi
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top