कैसे निर्देशिका की जाँच करने के लिए खाली है


16

मुझे एक आवश्यकता है, अगर मैं ./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.


क्या कोई कृपया एक बार मेरा कोड देख सकता है। मैंने इसे संपादित किया
बुद्ध श्रीकांत ने

जवाबों:


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

उपरोक्त POSIX- संगत परीक्षण है - और बहुत तेज़ होना चाहिए। lsएक निर्देशिका को छोड़कर सभी फाइलों / डायरियों को सूचीबद्ध करेगा .और ..प्रत्येक एक पंक्ति को छोड़कर और -qआउटपुट में सभी गैर-मुद्रण योग्य वर्णों ( \newlines को शामिल करने के लिए ) को एक ?प्रश्न-चिह्न के साथ सूचीबद्ध करेगा । इस तरह से यदि grepइनपुट में एक भी वर्ण प्राप्त होता है, तो यह सही होगा - और गलत।

इसे अकेले POSIX-shell में करने के लिए:

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 खोल (जो पहले नहीं अक्षम है -filename पीढ़ी) होगा या तो शाब्दिक के बाद तार करने के लिए स्थितीय पैरामीटर सरणी आदेश ऊपर, वरना प्रत्येक के अंत में ग्लोब ऑपरेटरों द्वारा उत्पन्न क्षेत्रों के लिए। क्या ऐसा करना इस बात पर निर्भर है कि ग्लब्स वास्तव में किसी भी चीज से मेल खाते हैं या नहीं। कुछ गोले में आप अशक्त ग्लोब को शून्य तक विस्तारित करने का निर्देश दे सकते हैं - या कुछ भी नहीं। यह कभी-कभी लाभकारी हो सकता है, लेकिन यह पोर्टेबल नहीं है और अक्सर अतिरिक्त समस्याओं के साथ आता है - जैसे कि विशेष शेल-विकल्प सेट करना और बाद में उन्हें परेशान करना।set"$@"set

शून्य-मूल्यवान तर्कों को संभालने का एकमात्र पोर्टेबल साधन या तो खाली या परेशान चर या ~टिल्ड-विस्तार शामिल है। और बाद में, पहले की तुलना में अधिक सुरक्षित है।

शेल के ऊपर केवल -eएक्सिस्टेंस के लिए किसी भी फाइल का परीक्षण किया जाता है, यदि निर्दिष्ट तीन ग्लोब में से कोई भी एक एकल मैच से अधिक नहीं होता है। इसलिए forलूप को कभी भी केवल तीन या उससे कम पुनरावृत्तियों के लिए चलाया जाता है, और केवल एक खाली निर्देशिका के मामले में, या इस मामले में कि एक या अधिक पैटर्न केवल एक ही फ़ाइल में हल होते हैं। forयह भी breakहै कि अगर globs में से किसी एक वास्तविक फ़ाइल का प्रतिनिधित्व करते हैं - और के रूप में मैं संभावना कम से कम करने के लिए सबसे के क्रम में globs की व्यवस्था की है संभावना है, यह बहुत ज्यादा हर बार पहले यात्रा पर छोड़ देना चाहिए।

किसी भी तरह से आप इसे केवल एक ही सिस्टम stat()कॉल में शामिल करना चाहिए - खोल और lsदोनों को केवल stat()निर्देशिका की आवश्यकता होनी चाहिए और फाइलों को सूचीबद्ध करना चाहिए जिसमें इसकी डेंट्री रिपोर्ट होती है जिसमें यह शामिल है। यह उस व्यवहार के विपरीत है, findजिसके बजाय stat()प्रत्येक फ़ाइल जिसे आप इसके साथ सूचीबद्ध कर सकते हैं।


पुष्टि कर सकते हैं। मैंने लिखी गई लिपियों में mikeserv के प्रारंभिक उदाहरण का उपयोग किया है।
ओथियस

यह खाली निर्देशिकाओं के रूप में रिपोर्ट करेगा जो मौजूद नहीं है या जिसके लिए आपके पास पहुंच नहीं है। दूसरे के लिए, यदि आपने एक्सेस पढ़ा है, लेकिन खोज एक्सेस नहीं है, तो YMMV।
स्टीफन चेज़लस

@ StéphaneChazelas - शायद, लेकिन इस तरह की रिपोर्टों के साथ उपयोगकर्ता को सूचित करने के लिए त्रुटि संदेश होंगे।
15

1
केवल lsमामले में। ग्लब्स और [चुप हैं जब उनके पास पहुंच नहीं है। उदाहरण के लिए, [ -e /var/spool/cron/crontabs/stephane ]चुपचाप गलत रिपोर्ट करेगा, जबकि उस नाम से एक फ़ाइल है।
स्टीफन चेज़लस

यह भी ध्यान दें कि यदि किसी निर्देशिका में सहानुभूति है (या निर्देशिका नहीं है) तो वे समाधान समतुल्य नहीं हैं।
स्टीफन चेज़लस

7

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 ]यदि dirरिक्त है, तो यह सच है और अधिकांश अन्य मूल्यों के लिए गलत है dir, लेकिन यह अविश्वसनीय है, उदाहरण के लिए यदि यह सही dirहै = -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 ") समझा सकते हैं। कोष्ठक में और बाहर $ क्या करता है?
बुद्ध श्रीकांत


@ गिल्स "$ (ls -A -" $ dir ")" अपनी फेंकने की त्रुटि को काम नहीं कर रहा है।
बुद्ध श्रीकांत

@buddhasreekanth क्या त्रुटि है? कॉपी पेस्ट। आप लोगों से मदद की उम्मीद नहीं कर सकते हैं यदि आप सिर्फ "काम नहीं कर रहे हैं" कहे बिना आपको बताए कि आप क्या निरीक्षण करते हैं।
गिल्स एसओ-

@ यह त्रुटि है। जब मैंने अपनी स्क्रिप्ट निष्पादित की है। / 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

मेरा जवाब है:

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 आज्ञाकारी है, और यह नहीं कि यह बहुत मायने रखता है, यह आमतौर पर उस समाधान की तुलना में तेज़ है जो निर्देशिका को सूचीबद्ध करता है और आउटपुट को गपशप करने के लिए पाइप करता है।

उपयोग:

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

यह निर्देशिका को सूचीबद्ध करता है और परिणाम को संक्षिप्त करने के लिए पाइप करता है। इसके बजाय, मैं एक साधारण फ़ंक्शन का प्रस्ताव करता हूं जो ग्लोब विस्तार और तुलना पर आधारित है।

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

यह फ़ंक्शन मानक POSIX नहीं है और इसके साथ एक सबहेल कॉल करता है $()। मैं इस सरल कार्य को पहले समझाता हूं ताकि हम अंतिम समाधान को बेहतर ढंग से समझ सकें (बाद में tldr उत्तर देखें)।

स्पष्टीकरण:

बाएं हाथ की ओर (LHS) खाली है जब कोई विस्तार नहीं होता है, जो कि ऐसा होता है जब निर्देशिका खाली होती है। नलग्लोब विकल्प की आवश्यकता है क्योंकि अन्यथा जब कोई मैच नहीं होता है, तो गोला ही विस्तार का परिणाम है। (RHS होने पर LHS के ग्लोब से मेल खाता है, जब निर्देशिका खाली होती है तो झूठी सकारात्मकता के कारण काम नहीं करता है, जब LHS ग्लोब ग्लोब के रूप में नामित एकल फ़ाइल से मेल खाता है: *ग्लोब *में फ़ाइल नाम के विकल्प में मेल खाता है । ) ब्रेस अभिव्यक्ति {,.[^.],..?}छिपी हुई फ़ाइलों को कवर करती है, लेकिन नहीं ..या नहीं .

क्योंकि shopt -s nullglobअंदर $()(एक उपधारा) निष्पादित किया जाता है , यह nullglobवर्तमान शेल के विकल्प को नहीं बदलता है , जो सामान्य रूप से एक अच्छी बात है। दूसरी ओर, इस विकल्प को स्क्रिप्ट में सेट करना एक अच्छा विचार है, क्योंकि जब कोई मैच नहीं होता है, तो एक ग्लोब रिटर्न कुछ होने की त्रुटि होती है। इसलिए, कोई स्क्रिप्ट की शुरुआत में नलग्लोब विकल्प सेट कर सकता है और फ़ंक्शन में इसकी आवश्यकता नहीं होगी। आइए इसे ध्यान में रखें: हम एक समाधान चाहते हैं जो नलग्लोब विकल्प के साथ काम करता है।

चेतावनियां:

यदि हमारे पास निर्देशिका तक पहुंच नहीं है, तो फ़ंक्शन उसी तरह रिपोर्ट करता है जैसे कि कोई खाली निर्देशिका थी। यह उस फ़ंक्शन पर भी लागू होता है जो निर्देशिका को सूचीबद्ध करता है और आउटपुट को grep करता है।

shopt -s nullglobआदेश मानक POSIX नहीं है।

यह द्वारा बनाए गए उपधारा का उपयोग करता है $()। यह बड़ी बात नहीं है, लेकिन अगर हम इससे बच सकते हैं तो अच्छा है।

समर्थक:

ऐसा नहीं है कि यह वास्तव में मायने रखता है, लेकिन यह फ़ंक्शन पिछले एक की तुलना में चार गुना तेज है, प्रक्रिया के भीतर कर्नेल में बिताए गए सीपीयू समय की मात्रा के साथ मापा जाता है।

अन्य उपाय:

हम shopt -s nullglobLHS पर गैर POSIX कमांड को हटा सकते हैं और स्ट्रिंग "$1/* $1/.[^.]* $1/..?*"को RHS में डाल सकते हैं और अलग-अलग झूठी सकारात्मकता को समाप्त कर सकते हैं जो तब होती हैं जब हमारे पास केवल नाम वाली फाइलें होती हैं '*', .[^.]*या ..?*निर्देशिका में होती है:

function emptydir {
 [ "$(echo "$1"/{,.[^.],..?}*)" = "$1/* $1/.[^.]* $1/..?*" ] &&
 [ ! -e "$1/*" ] && [ ! -e "$1/.[^.]*" ] && [ ! -e "$1/..?*" ]
}

shopt -s nullglobकमांड के बिना , यह अब उपधारा को हटाने के लिए समझ में आता है, लेकिन हमें सावधान रहना होगा क्योंकि हम शब्द विभाजन से बचना चाहते हैं और फिर भी एलएचएस पर ग्लोब विस्तार की अनुमति देते हैं। विशेष रूप से शब्द विभाजन से बचने के लिए उद्धृत करने से काम नहीं चलता है, क्योंकि यह ग्लोब विस्तार को भी रोकता है। हमारे समाधान के लिए ग्लब्स पर अलग से विचार करना है:

function emptydir {
 [ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
 [ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
 [ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}

हमारे पास अभी भी व्यक्तिगत ग्लोब के लिए शब्द विभाजन है, लेकिन यह अब ठीक है, क्योंकि यह केवल एक त्रुटि के परिणामस्वरूप होगा जब निर्देशिका खाली नहीं होगी। LHS पर दिए गए ग्लोब से मेल खाती कई फाइलें होने पर हमने त्रुटि संदेश को छोड़ने के लिए 2> / dev / null जोड़ा।

हमें याद है कि हम एक समाधान चाहते हैं जो नलग्लोब विकल्प के साथ भी काम करता है। उपरोक्त समाधान नलग्लोब विकल्प के साथ विफल हो जाता है, क्योंकि जब निर्देशिका खाली होती है, तो एलएचएस भी खाली होता है। सौभाग्य से, यह कभी नहीं कहता कि निर्देशिका खाली है जब यह नहीं है। यह केवल यह कहने में विफल है कि यह कब है खाली है। इसलिए, हम nullglob विकल्प को अलग से प्रबंधित कर सकते हैं। हम केवल मामलों [ "$1/"* = "" ]आदि को जोड़ नहीं सकते क्योंकि ये विस्तार के रूप में होंगे [ = "" ], आदि जो वाक्य-रचना के गलत हैं। इसलिए, हम [ "$1/"* "" = "" ]इसके बजाय आदि का उपयोग करते हैं। हमें फिर से तीन मामलों पर विचार करना है *, ..?*और .[^.]*छिपी हुई फ़ाइलों का मिलान करना है, लेकिन नहीं .और..। यदि हमारे पास नलग्लोब विकल्प नहीं है, तो ये हस्तक्षेप नहीं करेंगे, क्योंकि वे यह भी कभी नहीं कहते हैं कि जब यह नहीं है तो यह खाली है। तो, अंतिम प्रस्तावित समाधान है:

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 *


$(set -f; echo "$1"/*)लेखन का जटिल तरीका लगता है "$1/*"। और यह छिपी निर्देशिका या फ़ाइलों से मेल नहीं खाएगा।
मुरु

क्या मैं मतलब में कोई फ़ाइल नाम विस्तार है कि वहाँ है "$1/*"(ध्यान दें उद्धरण शामिल *), इसलिए set -fऔर subshell के लिए अनावश्यक हैं कि विशेष रूप से।
मुरु

यह एक अलग प्रश्न का उत्तर देता है: क्या निर्देशिका में गैर-छिपी हुई फाइलें या निर्देशिकाएं हैं। उसके बारे में मुझे खेद है। मुझे इसे डिलीट करने में खुशी होगी।
डोमिनिक 101

1
वहाँ तरीकों से भी छिपा फ़ाइलों मैच के लिए (में जटिल ग्लोब देख रहे हैं mikeserv का जवाब : ./* ./.[!.]* ./..?*)। हो सकता है कि आप फ़ंक्शन को पूरा करने के लिए इसे शामिल कर सकते हैं।
मुरु

आह! मैं इसे देखूंगा और सीखूंगा और शायद हमारे पास ग्लोबिंग विस्तार के आधार पर एक पूर्ण उत्तर होगा!
डोमिनिक 101

0

यहाँ यह करने का एक और सरल तरीका है। मान लें कि D उस निर्देशिका का पूर्ण पथ नाम है जिसे आप शून्यता के लिए परीक्षण करना चाहते हैं।

फिर

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

यह काम करता है क्योंकि

du -s D

आउटपुट के रूप में है

size_of_D   D

awk डी हटाता है और यदि आकार 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

2
नमस्ते और साइट पर आपका स्वागत है। कृपया ऐसे उत्तर पोस्ट न करें जो केवल कोड हैं और कोई स्पष्टीकरण नहीं है। कोड टिप्पणी करें या अन्यथा समझाएं कि आप क्या कर रहे हैं। वैसे, उपयोग करने का कोई कारण नहीं है echo, बस आपको आवश्यकता है list="$somedir/*"। इसके अलावा, यह मुश्किल है और त्रुटियों के लिए प्रवण है। आपने उल्लेख नहीं किया है कि ओपी को लक्ष्य डायर का रास्ता देना चाहिए, जिसमें अनुगामी स्लेश शामिल है, उदाहरण के लिए।
terdon
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.