cara memeriksa direktori kosong

15

Saya memiliki persyaratan, jika saya menjalankan skrip ./123dengan argumen path kosong, katakan /usr/share/linux-headers-3.16.0-34-generic/.tmp_versions(direktori ini kosong). Seharusnya menampilkan "direktori kosong"

Kode saya adalah:

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

Saya memiliki tampilan kesalahan seperti, jika saya menjalankan nama skrip ./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions(direktori ini kosong).

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

alih-alih hanya menampilkan keluaran "direktori kosong" yang menunjukkan keluaran di atas

Output di bawah ini harus ditampilkan jika saya menampilkan skrip dengan argumen yang benar (maksud saya dengan jalur direktori yang benar). mengatakan./123 /usr/share

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

output yang saya harapkan adalah: ./123 /usr/src/linux-headers-3.16.0-34-generic/.tmp_versions

directory is empty.
buddha sreekanth
sumber
4
Lihat mywiki.wooledge.org/BashFAQ/004
Valentin Bajrami
dapat seseorang tolong periksa kode saya sekali. Saya mengeditnya
buddha sreekanth
Terkait: superuser.com/questions/352289/...
AlikElzin-kilaka

Jawaban:

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

Di atas adalah tes yang kompatibel dengan POSIX - dan harus sangat cepat. lsakan mencantumkan semua file / direktori dalam direktori kecuali .dan ..masing - masing per baris dan akan -qmencatat semua karakter yang tidak dapat dicetak (untuk memasukkan \nbaris baru) dalam output dengan ?tanda tanya. Dengan cara ini jika grepmenerima bahkan satu karakter dalam input itu akan mengembalikan true - else false.

Untuk melakukannya dalam shell POSIX saja:

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"

Sebuah POSIX-shell (yang memiliki tidak lebih awal dinonaktifkan -fgenerasi ilename) akan setdengan "$@"posisi-parameter array ke salah satu string literal diikuti olehset perintah di atas, atau yang lain untuk bidang yang dihasilkan oleh operator gumpal pada akhir masing-masing. Apakah itu tergantung pada apakah gumpalan benar-benar cocok dengan sesuatu. Dalam beberapa shell, Anda dapat menginstruksikan glob yang tidak menyelesaikan untuk memperluas ke null - atau tidak sama sekali. Ini kadang-kadang bisa bermanfaat, tetapi tidak portabel dan sering disertai dengan masalah tambahan - seperti harus mengatur opsi shell khusus dan kemudian menghapusnya.

Satu-satunya cara portabel untuk menangani argumen bernilai nol melibatkan variabel kosong atau tidak disetel atau ~ekspansi tilde. Dan yang terakhir, omong-omong, jauh lebih aman daripada yang pertama.

Di atas shell hanya menguji salah satu file untuk -existence jika tidak satu pun dari tiga gumpalan yang ditentukan menyelesaikan lebih dari satu pertandingan. Jadi forloop hanya dijalankan sama sekali untuk tiga atau lebih sedikit iterasi, dan hanya dalam kasus direktori kosong, atau dalam kasus satu atau lebih pola hanya terpecahkan untuk satu file. The forjuga breaks jika salah satu gumpalan mewakili file yang sebenarnya - dan seperti yang saya telah mengatur gumpalan di urutan paling mungkin untuk paling tidak mungkin, harus cukup banyak berhenti pada iterasi pertama setiap kali.

Apa pun yang Anda lakukan, itu harus melibatkan hanya satu stat()panggilan sistem - shell dan lsharus keduanya hanya perlu ke stat()direktori yang ditanyai dan daftar file-file laporan giginya yang dikandungnya. Ini kontras dengan perilaku findyang sebaliknya akan stat()setiap file Anda mungkin daftar dengan itu.

mikeserv
sumber
Dapat mengkonfirmasi. Saya menggunakan contoh awal mikeserv dalam skrip yang saya tulis.
Otheus
Itu akan dilaporkan sebagai direktori kosong yang tidak ada atau yang Anda tidak memiliki akses baca. Untuk yang kedua, jika Anda memiliki akses baca tetapi tidak mencari akses, YMMV.
Stéphane Chazelas
@ StéphaneChazelas - mungkin, tetapi laporan tersebut akan disertai dengan pesan kesalahan untuk memberi tahu pengguna tersebut.
mikeserv
1
Hanya dalam lskasus ini. gumpal dan [diam ketika mereka tidak memiliki akses. Sebagai contoh, [ -e /var/spool/cron/crontabs/stephane ]diam-diam akan melaporkan palsu, sementara ada file dengan nama itu.
Stéphane Chazelas
Perhatikan juga bahwa solusi tersebut tidak setara jika somedir adalah symlink ke direktori (atau bukan direktori).
Stéphane Chazelas
6

Dengan GNU atau BSD modern find, Anda dapat melakukan:

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

(mengasumsikan $dirtidak terlihat seperti sebuah findpredikat ( !, (, -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
Stéphane Chazelas
sumber
4

[-z $dir ]mengeluh bahwa tidak ada perintah yang dipanggil [-zpada kebanyakan sistem. Anda membutuhkan ruang di sekitar tanda kurung .

[ -z $dir ]kebetulan benar jika dirkosong, dan salah untuk sebagian besar nilai lain dir, tetapi tidak dapat diandalkan, misalnya benar jika nilai diradalah = -zatau -o -o -n -n. Selalu gunakan tanda kutip ganda di sekitar substitusi perintah (ini berlaku juga untuk seluruh skrip Anda).

[ -z "$dir" ]menguji apakah nilai variabel dirkosong. Nilai variabel adalah string, yang merupakan jalur ke direktori. Itu tidak memberi tahu Anda apa pun tentang direktori itu sendiri.

Tidak ada operator untuk menguji apakah direktori kosong, seperti ada untuk file biasa ( [ -s "$dir" ]berlaku untuk direktori bahkan jika itu kosong). Cara sederhana untuk menguji apakah direktori kosong adalah dengan membuat daftar isinya; jika Anda mendapatkan teks kosong, direktori tersebut kosong.

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

Pada sistem lama yang tidak memiliki ls -A, Anda dapat menggunakan ls -a, tetapi kemudian .dan ..terdaftar.

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

(Jangan membuat indentasi baris yang dimulai ..karena string harus berisi hanya baris baru, bukan baris baru dan ruang tambahan.)

Gilles 'SANGAT berhenti menjadi jahat'
sumber
tolong jelaskan bagian ini "$ (ls -A -" $ dir ")". apa yang dilakukan $ dalam dan di luar kurung?
buddha sreekanth
@buddhasreekanth $(…)adalah pengganti perintah
Gilles 'SO- stop being evil'
@Giles "$ (ls -A -" $ dir ")" tidak berfungsi karena kesalahan melemparnya.
buddha sreekanth
@buddhasreekanth Apa kesalahannya? Salin-tempel. Anda tidak dapat mengharapkan orang untuk membantu Anda jika Anda hanya mengatakan "tidak bekerja" tanpa menjelaskan apa yang Anda amati.
Gilles 'SANGAT berhenti menjadi jahat'
@ Giles ini kesalahannya. Ketika saya menjalankan skrip saya ./filestats kesalahan lemparnya .. $ / filestats / home / sreekanth / testdir / aki / schatz ukuran file minimum: 4096 / home / sreekanth / testdir / aki / schatz ukuran file maksimum: 4096 / home / sreekanth / testdir / aki / schatz ukuran file rata-rata: 4 direktori kosong. Alih-alih menunjukkan "direktori kosong". Ditampilkan dengan ukuran minimum, ukuran maks, dan ukuran rata-rata.
buddha sreekanth
3

Anda sedang melihat atribut direktori itu sendiri.

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

Anda ingin menghitung berapa banyak file di sana:

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

Jadi, tes untuk "direktori kosong" adalah:

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

Seperti ini:

$ 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
glenn jackman
sumber
Itu akan melaporkan direktori kosong jika Anda tidak memiliki akses untuk membacanya atau tidak ada.
Stéphane Chazelas
1

Jawaban tldr saya adalah:

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/..?*" ]
}

Ini sesuai dengan POSIX dan, tidak terlalu penting, biasanya lebih cepat daripada solusi yang mencantumkan direktori dan menyalurkan output ke grep.

Pemakaian:

if emptydir adir
then
  echo "nothing found" 
else 
  echo "not empty" 
fi

Saya suka jawabannya https://unix.stackexchange.com/a/202276/160204 , yang saya tulis ulang sebagai:

function emptydir {
  ! { ls -1qA "./$1/" | grep -q . ; }
}

Ini daftar direktori dan pipa hasilnya ke grep. Sebagai gantinya, saya mengusulkan fungsi sederhana yang didasarkan pada ekspansi dan perbandingan glob.

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

Fungsi ini bukan POSIX standar dan memanggil subkulit dengan $(). Saya menjelaskan fungsi sederhana ini terlebih dahulu sehingga kita dapat lebih memahami solusi akhir (lihat jawaban tldr di atas) nanti.

Penjelasan:

Sisi kiri (LHS) kosong ketika tidak ada ekspansi, yang merupakan kasus ketika direktori kosong. Opsi nullglob diperlukan karena jika tidak ada kecocokan, glob itu sendiri adalah hasil dari ekspansi. (Memiliki RHS cocok dengan gumpalan LHS ketika direktori kosong tidak bekerja karena positif palsu yang terjadi ketika LHS glob cocok dengan satu file bernama glob itu sendiri: the *in the glob cocok dengan substring *pada nama file. ) Ekspresi penjepit {,.[^.],..?}mencakup file tersembunyi, tetapi tidak ..atau ..

Karena shopt -s nullglobdijalankan di dalam $()(subkulit), itu tidak mengubah nullglobopsi shell saat ini, yang biasanya merupakan hal yang baik. Di sisi lain, itu ide yang baik untuk mengatur opsi ini dalam skrip, karena itu adalah kesalahan untuk memiliki gumpalan mengembalikan sesuatu ketika tidak ada kecocokan. Jadi, seseorang dapat mengatur opsi nullglob di awal skrip dan itu tidak akan diperlukan dalam fungsinya. Ingatlah ini: kami menginginkan solusi yang berfungsi dengan opsi nullglob.

Peringatan:

Jika kita tidak memiliki akses baca ke direktori, fungsinya melaporkan sama seperti jika ada direktori kosong. Ini berlaku juga untuk fungsi yang mencantumkan direktori dan menerima output.

The shopt -s nullglobperintah tidak standar POSIX.

Ini menggunakan subkulit yang dibuat oleh $(). Ini bukan masalah besar, tapi menyenangkan jika kita bisa menghindarinya.

Pro:

Bukan berarti itu penting, tetapi fungsi ini empat kali lebih cepat dari yang sebelumnya, diukur dengan jumlah waktu CPU yang dihabiskan di kernel dalam proses.

Solusi lain:

Kita dapat menghapus perintah non POSIX shopt -s nullglobpada LHS dan meletakkan string "$1/* $1/.[^.]* $1/..?*"di RHS dan menghilangkan secara terpisah positif palsu yang terjadi ketika kita hanya memiliki file bernama '*', .[^.]*atau ..?*di direktori:

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

Tanpa shopt -s nullglobperintah, sekarang masuk akal untuk menghapus subkulit, tetapi kita harus berhati-hati karena kita ingin menghindari pemisahan kata dan belum mengizinkan ekspansi global pada LHS. Secara khusus mengutip untuk menghindari pemisahan kata tidak berfungsi, karena itu juga mencegah ekspansi gumpal. Solusi kami adalah mempertimbangkan gumpalan secara terpisah:

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

Kami masih memiliki pemisahan kata untuk glob individu, tetapi tidak apa-apa sekarang, karena itu hanya akan menghasilkan kesalahan ketika direktori tidak kosong. Kami menambahkan 2> / dev / null, untuk membuang pesan kesalahan ketika ada banyak file yang cocok dengan bola yang diberikan pada LHS.

Kami ingat bahwa kami menginginkan solusi yang bekerja dengan opsi nullglob juga. Solusi di atas gagal dengan opsi nullglob, karena ketika direktori kosong, LHS juga kosong. Untungnya, tidak pernah dikatakan bahwa direktori tersebut kosong ketika tidak. Itu hanya gagal untuk mengatakan bahwa itu kosong ketika itu. Jadi, kita dapat mengelola opsi nullglob secara terpisah. Kami tidak dapat dengan mudah menambahkan kasus [ "$1/"* = "" ]dll. Karena ini akan berkembang sebagai [ = "" ], dll. Yang secara sintaksis salah. Jadi, kami menggunakan [ "$1/"* "" = "" ]dll. Kita lagi harus mempertimbangkan tiga kasus *, ..?*dan .[^.]*untuk mencocokkan file yang tersembunyi, tetapi tidak .dan... Ini tidak akan mengganggu jika kita tidak memiliki opsi nullglob, karena mereka juga tidak pernah mengatakan bahwa itu kosong ketika tidak ada. Jadi, solusi akhir yang diusulkan adalah:

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/..?*" ]
}

Masalah keamanan:

Buat dua file rmdan xdalam direktori kosong dan jalankan *pada prompt. Glob *akan meluas ke rm xdan ini akan dieksekusi untuk menghapus x. Ini bukan masalah keamanan, karena dalam fungsi kami, gumpalan terletak di mana ekspansi tidak dilihat sebagai perintah, tetapi sebagai argumen, seperti di for f in *.

Dominic108
sumber
$(set -f; echo "$1"/*)sepertinya cara penulisan yang agak rumit "$1/*". Dan ini tidak akan cocok dengan direktori atau file tersembunyi.
muru
Yang saya maksudkan adalah bahwa tidak ada ekspansi nama file di "$1/*"(perhatikan tanda kutip termasuk *), sehingga set -fdan subkulit tidak perlu untuk itu pada khususnya.
muru
Ini menjawab pertanyaan yang berbeda: apakah direktori tersebut berisi file atau direktori yang tidak tersembunyi. Saya turut berduka atas hal itu. Saya akan dengan senang hati menghapusnya.
Dominic108
1
Ada cara untuk mencocokkan file tersembunyi juga (lihat gumpal rumit di jawaban mikeserv ini : ./* ./.[!.]* ./..?*). Mungkin Anda bisa memasukkan itu untuk membuat fungsi lengkap.
muru
Ah ! Saya akan melihat ini dan belajar dan mungkin kita akan memiliki jawaban lengkap berdasarkan ekspansi globbing!
Dominic108
0

Inilah cara sederhana lain untuk melakukannya. Asumsikan D adalah nama path lengkap dari direktori yang ingin Anda uji kekosongan.

Kemudian

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

Ini berhasil karena

du -s D

memiliki sebagai output

size_of_D   D

awk menghapus D dan jika ukurannya 0 direktori kosong.

Richard Gostanian
sumber
-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
pengguna137815
sumber
2
Hai dan selamat datang di situs ini. Tolong jangan posting jawaban yang hanya kode dan tidak ada penjelasan. Komentari kodenya atau jelaskan apa yang sedang Anda lakukan. Ngomong-ngomong, tidak ada alasan untuk menggunakannya echo, yang Anda butuhkan hanyalah list="$somedir/*". Juga, ini rumit dan rentan terhadap kesalahan. Anda belum menyebutkan bahwa OP harus memberikan jalur target dir termasuk garis miring, misalnya.
terdon