Bash scripting: test untuk direktori kosong

95

Saya ingin menguji apakah direktori tidak mengandung file apa pun. Jika demikian, saya akan melewati beberapa pemrosesan.

Saya mencoba yang berikut ini:

if [ ./* == "./*" ]; then
    echo "No new file"
    exit 1
fi

Itu memberikan kesalahan berikut:

line 1: [: too many arguments

Apakah ada solusi / alternatif?

Anthony Kong
sumber
terkait: stackoverflow.com/q/91368/52074
Trevor Boyd Smith

Jawaban:

121
if [ -z "$(ls -A /path/to/dir)" ]; then
   echo "Empty"
else
   echo "Not Empty"
fi

Juga, akan lebih baik untuk memeriksa apakah direktori sudah ada sebelumnya

Andrey Atapin
sumber
11
Jangan gunakan &&dan ||secara bersamaan! Jika echo "Not Empty"gagal, echo "Empty"akan berjalan! Coba echo "test" && false || echo "fail"! Ya, saya tahu echotidak akan gagal tetapi jika Anda mengubah perintah lain, Anda akan terkejut!
uzsolt
4
Tolong, berikan setidaknya satu contoh ketika kode di atas tidak berfungsi. Konkretnya kode ini benar sekali. Saya harap penanya dapat mengadaptasi ini untuk keperluannya sendiri
Andrey Atapin
3
[ "$(ls -A /)" ] && touch /non-empty || echo "Empty"- jika Anda ingin "menandai" direktori yang tidak kosong dengan file yang dibuat dengan nama non-empty, akan gagal dan mencetak "Kosong".
uzsolt
4
di mana touch /emptydi baris saya?
Andrey Atapin
7
Oh tidak! Ada masalah yang sangat penting dengan kode ini. Seharusnya if [ -n "$(ls -A /path/to/dir)" ]; then... Harap perbarui jawabannya sebelum seseorang menempelkan kode ini ke server mereka di suatu tempat dan seorang hacker mencari cara untuk mengeksploitasinya. Jika /path/to/dirtidak kosong, maka nama file yang ada dapat diteruskan sebagai argumen /bin/testyang jelas tidak dimaksudkan. Cukup tambahkan -nargumen, masalah terpecahkan. Terima kasih!
Edward Ned Harvey
21

Tidak perlu menghitung apa pun atau gumpalan shell. Anda juga dapat menggunakan readdalam kombinasi dengan find. Jika findoutput kosong, Anda akan kembali false:

if find /some/dir -mindepth 1 | read; then
   echo "dir not empty"
else
   echo "dir empty"
fi

Ini harus portabel.

slhck
sumber
Solusi yang bagus, tapi saya pikir echopanggilan Anda mencerminkan hasil yang salah: dalam pengujian saya (di bawah Cygwin) find . -mindepth 1 | readmemiliki 141 kode kesalahan dalam direktori yang tidak kosong, dan 0 di direktori kosong
Lucas Cimon
@LucasCimon Tidak di sini (macOS dan GNU / Linux). Untuk direktori yang tidak kosong, readmengembalikan 0, dan untuk yang kosong, 1.
slhck
1
PSA tidak bekerja denganset -o pipefail
Kolonel Thirty Two
19
if [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; then
    echo "Empty directory"
else
    echo "Not empty or NOT a directory"
fi
uzsolt
sumber
Benar dan cepat. Bagus!
l0b0
4
Dibutuhkan tanda kutip (2x) dan pengujiannya -nbenar dan aman (uji dengan direktori dengan spasi di namanya, uji dengan direktori tidak kosong dengan nama '0 = 1'). ... [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; ...
Zrin
1
@ivan_pozdeev Itu tidak benar, setidaknya untuk menemukan GNU. Anda mungkin berpikir grep. serverfault.com/questions/225798/...
Vladimir Panteleev
Mungkin lebih mudah untuk menulis find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty | grep ., dan bergantung pada status keluar dari grep. Apa pun cara Anda melakukannya, ini adalah jawaban yang tepat untuk pertanyaan ini.
Tom Anderson
13
#!/bin/bash
if [ -d /path/to/dir ]; then
    # the directory exists
    [ "$(ls -A /path/to/dir)" ] && echo "Not Empty" || echo "Empty"
else
    # You could check here if /path/to/dir is a file with [ -f /path/to/dir]
fi
Renaud
sumber
4
Pasti itu, tidak perlu parsing output ls, lihat saja apakah itu kosong atau tidak. Menggunakan find hanya terasa seperti berlebihan bagi saya.
akostadinov
4

Ini akan melakukan pekerjaan di direktori kerja saat ini (.):

[ `ls -1A . | wc -l` -eq 0 ] && echo "Current dir is empty." || echo "Current dir has files (or hidden files) in it."

atau perintah yang sama terpecah pada tiga baris hanya agar lebih mudah dibaca:

[ `ls -1A . | wc -l` -eq 0 ] && \
echo "Current dir is empty." || \
echo "Current dir has files (or hidden files) in it."

Ganti saja ls -1A . | wc -ldengan ls -1A <target-directory> | wc -ljika Anda perlu menjalankannya di folder target yang berbeda.

Sunting : Saya diganti -1adengan -1A(lihat komentar @aniel)

ztank1013
sumber
2
gunakan ls -Asaja. Beberapa sistem file tidak memiliki .dan ..tautan simbolik menurut dokumen.
Daniel Beck
1
Terima kasih @aniel, saya mengedit jawaban saya setelah saran Anda. Saya tahu "1" mungkin dihapus juga.
ztank1013
2
Tidak sakit, tetapi tersirat jika output tidak ke terminal. Karena Anda menyalurkannya ke program lain, itu berlebihan.
Daniel Beck
-1pasti berlebihan. Bahkan jika lstidak akan mencetak satu item per baris ketika akan disalurkan maka itu tidak mempengaruhi gagasan untuk memeriksa apakah itu menghasilkan nol atau lebih baris.
Victor Yarema
3

Gunakan yang berikut ini:

count="$( find /path -mindepth 1 -maxdepth 1 | wc -l )"
if [ $count -eq 0 ] ; then
   echo "No new file"
   exit 1
fi

Dengan cara ini, Anda terlepas dari format output ls. -mindepthmelompati direktori itu sendiri, -maxdepthmencegah secara rekursif mempertahankan ke dalam subdirektori untuk mempercepat.

Daniel Beck
sumber
Tentu saja, Anda sekarang bergantung pada wc -ldan findformat output (yang cukup sederhana sekalipun).
Daniel Beck
3

Menggunakan array:

files=( * .* )
if (( ${#files[@]} == 2 )); then
    # contents of files array is (. ..)
    echo dir is empty
fi
glenn jackman
sumber
3
Solusi yang sangat bagus, tetapi perlu diperhatikanshopt -s nullglob
xebeche
3
The ${#files[@]} == 2asumsi tidak berdiri untuk dir root (Anda mungkin tidak akan menguji apakah itu kosong tapi beberapa kode yang tidak tahu tentang batasan yang mungkin).
ivan_pozdeev
3

Cara PID bebas hack, tapi khusus, hanya:

is_empty() {
    test -e "$1/"* 2>/dev/null
    case $? in
        1)   return 0 ;;
        *)   return 1 ;;
    esac
}

Ini mengambil keuntungan dari fakta bahwa testbuiltin keluar dengan 2 jika diberikan lebih dari satu argumen setelah -e: Pertama, "$1"/*bola diperluas oleh bash. Ini menghasilkan satu argumen per file. Begitu

  • Jika tidak ada file, tanda bintang di test -e "$1"*tidak berkembang, jadi Shell kembali ke mencoba file bernama *, yang mengembalikan 1.

  • ... kecuali jika sebenarnya ada satu file yang dinamai persis *, maka asterisk mengembang dengan baik, asterisk, yang berakhir dengan panggilan yang sama seperti di atas, yaitu. test -e "dir/*", kali ini mengembalikan 0. (Terima kasih @ TrueY telah menunjukkan ini.)

  • Jika ada satu file, test -e "dir/file"jalankan, yang mengembalikan 0.

  • Tetapi jika ada lebih banyak file dari 1, test -e "dir/file1" "dir/file2" dijalankan, yang bash melaporkannya sebagai kesalahan penggunaan, yaitu 2.

case membungkus seluruh logika sekitar sehingga hanya kasus pertama, dengan 1 status keluar dilaporkan berhasil.

Kemungkinan masalah yang belum saya periksa:

  • Ada lebih banyak file daripada jumlah argumen yang diperbolehkan - Saya kira ini bisa berperilaku mirip dengan kasus dengan 2+ file.

  • Atau sebenarnya ada file dengan nama kosong - Saya tidak yakin itu mungkin pada OS waras / FS.

Alois Mahdal
sumber
1
Koreksi kecil: jika tidak ada file dalam dir /, maka test -e dir/*dipanggil. Jika satu-satunya file adalah '*' dalam dir maka tes akan mengembalikan 0. Jika ada lebih banyak file, maka itu mengembalikan 2. Jadi itu berfungsi seperti yang dijelaskan.
Benar,
Anda benar, @TrueY, saya sudah memasukkannya ke dalam jawabannya. Terima kasih!
Alois Mahdal
2

Dengan FIND (1) (di Linux dan FreeBSD) Anda dapat melihat entri direktori secara non-rekursif melalui "-maxdepth 0" dan menguji apakah itu kosong dengan "-empty". Diterapkan pada pertanyaan ini memberikan:

if test -n "$(find ./ -maxdepth 0 -empty)" ; then
    echo "No new file"
    exit 1
fi
TimJ
sumber
Mungkin tidak 100% portabel, tetapi elegan.
Craig Ringer
1

Saya pikir solusi terbaik adalah:

files=$(shopt -s nullglob; shopt -s dotglob; echo /MYPATH/*)
[[ "$files" ]] || echo "dir empty" 

terima kasih kepada https://stackoverflow.com/a/91558/520567

Ini adalah pengeditan anonim dari jawaban saya yang mungkin atau mungkin tidak bermanfaat bagi seseorang: Sedikit perubahan memberikan jumlah file:

files=$(shopt -s nullglob dotglob; s=(MYPATH/*); echo ${s[*]}) 
echo "MYPATH contains $files files"

Ini akan berfungsi dengan benar bahkan jika nama file mengandung spasi.

akostadinov
sumber
1
if find "${DIR}" -prune ! -empty -exit 1; then
    echo Empty
else
    echo Not Empty
fi

EDIT: Saya pikir solusi ini berfungsi baik dengan gnu find, setelah sekilas melihat implementasinya . Tetapi ini mungkin tidak bekerja dengan, misalnya, temuan netbsd . Memang, yang satu menggunakan bidang st_size stat (2). Manual menggambarkannya sebagai:

st_size            The size of the file in bytes.  The meaning of the size
                   reported for a directory is file system dependent.
                   Some file systems (e.g. FFS) return the total size used
                   for the directory metadata, possibly including free
                   slots; others (notably ZFS) return the number of
                   entries in the directory.  Some may also return other
                   things or always report zero.

Solusi yang lebih baik, juga lebih sederhana, adalah:

if find "${DIR}" -mindepth 1 -exit 1; then
    echo Empty
else
    echo Not Empty
fi

Juga, -prune dalam solusi pertama tidak berguna.

EDIT: no -exit for gnu find .. solusi di atas bagus untuk temuan NetBSD. Untuk menemukan GNU, ini harus bekerja:

if [ -z "`find \"${DIR}\" -mindepth 1 -exec echo notempty \; -quit`" ]; then
    echo Empty
else
    echo Not Empty
fi
teriak
sumber
find from GNU findutils 4.6.0 (versi terbaru) tidak memiliki -exitpredikat .
Dennis
0

Ini semua adalah hal yang luar biasa - cukup buat menjadi skrip sehingga saya dapat memeriksa direktori kosong di bawah ini. Di bawah ini harus dimasukkan ke dalam file yang disebut 'findempty', ditempatkan di jalan di suatu tempat sehingga bash dapat menemukannya dan kemudian chmod 755 untuk dijalankan. Dapat dengan mudah diubah untuk kebutuhan spesifik Anda, saya kira.

#!/bin/bash
if [ "$#" == "0" ]; then 
find . -maxdepth 1 -type d -exec findempty "{}"  \;
exit
fi

COUNT=`ls -1A "$*" | wc -l`
if [ "$COUNT" == "0" ]; then 
echo "$* : $COUNT"
fi
Warren Sherliker
sumber
0

Ini berfungsi untuk saya, untuk memeriksa & memproses file dalam direktori ../IN, mengingat skrip ada di ../Scriptdirektori:

FileTotalCount=0

    for file in ../IN/*; do
    FileTotalCount=`expr $FileTotalCount + 1`
done

if test "$file" = "../IN/*"
then

    echo "EXITING: NO files available for processing in ../IN directory. "
    exit

else

  echo "Starting Process: Found ""$FileTotalCount"" files in ../IN directory for processing."

# Rest of the Code
Arijit
sumber
0

Bagaimana dengan pengujian jika direktori ada dan tidak kosong dalam pernyataan if

if [[ -d path/to/dir && -n "$(ls -A path/to/dir)" ]]; then 
  echo "directory exists"
else
  echo "directory doesn't exist"
fi
stanieviv
sumber
-1

Untuk direktori apa pun selain yang sekarang, Anda dapat memeriksa apakah itu kosong dengan mencobanya rmdir, karena rmdirdijamin gagal untuk direktori yang tidak kosong. Jika rmdirberhasil, dan Anda benar-benar ingin direktori kosong untuk bertahan dalam ujian, cukup lakukan mkdirlagi.

Jangan gunakan peretasan ini jika ada proses lain yang mungkin menjadi disobobobulasi oleh direktori yang mereka ketahui tentang penghentian sebentar.

Jika rmdirtidak bekerja untuk Anda, dan Anda mungkin sedang menguji direktori yang berpotensi mengandung banyak file, solusi apa pun yang mengandalkan shell globbing bisa menjadi lambat dan / atau mengalami batas panjang baris perintah. Mungkin lebih baik digunakan finddalam kasus itu. findSolusi tercepat yang bisa saya pikirkan berjalan seperti

is_empty() {
    test -z $(find "$1" -mindepth 1 -printf X -quit)
}

Ini berfungsi untuk versi GNU dan BSD findtetapi tidak untuk Solaris, yang tidak memiliki semua findoperator tersebut. Cintai pekerjaanmu, Oracle.

flabdablet
sumber
Bukan ide yang bagus. OP hanya ingin menguji apakah direktori itu kosong atau tidak.
roaima
-3

Anda dapat mencoba menghapus direktori dan menunggu kesalahan; rmdir tidak akan menghapus direktori jika tidak kosong.

_path="some/path"
if rmdir $_path >/dev/null 2>&1; then
   mkdir $_path        # create it again
   echo "Empty"
else
   echo "Not empty or doesn't exist"
fi
impxd
sumber
3
-1 Ini adalah jenis kode yang menjadi bumerang. rmdirakan gagal jika saya tidak memiliki izin untuk menghapus direktori; atau jika itu adalah subvolume Btrfs; atau jika itu milik sistem file read-only. Dan jika rmdirtidak gagal dan mkdirberjalan: bagaimana jika direktori yang sudah dihapus milik pengguna lain? bagaimana dengan izinnya (mungkin non-standar)? ACL? atribut yang diperluas? Semua hilang.
Kamil Maciorowski
Yah, saya baru belajar bash dan saya pikir itu bisa menjadi cara yang lebih cepat daripada mengulangi semua direktori, tetapi CPU kuat dan Anda benar, tidak aman.
impxd