Bash scripting: kiểm tra thư mục trống


94

Tôi muốn kiểm tra nếu một thư mục không chứa bất kỳ tập tin nào. Nếu vậy, tôi sẽ bỏ qua một số xử lý.

Tôi đã thử như sau:

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

Điều đó đưa ra lỗi sau:

line 1: [: too many arguments

Có một giải pháp / thay thế?


Câu trả lời:


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

Ngoài ra, sẽ rất tuyệt nếu kiểm tra xem thư mục có tồn tại trước đó không.


11
Đừng sử dụng &&||đồng thời! Nếu echo "Not Empty"thất bại, echo "Empty"sẽ chạy! Hãy thử echo "test" && false || echo "fail"! Vâng, tôi biết echosẽ không thất bại nhưng nếu bạn thay đổi bất kỳ lệnh nào khác, bạn sẽ bất ngờ!
uzsolt

4
Vui lòng cung cấp ít nhất một ví dụ khi đoạn mã trên không hoạt động. Cụ thể mã này là hoàn toàn chính xác. Tôi hy vọng rằng người hỏi có thể điều chỉnh điều này cho mục đích riêng của mình
Andrey Atapin

3
[ "$(ls -A /)" ] && touch /non-empty || echo "Empty"- nếu bạn muốn "đánh dấu" các thư mục không trống với một tệp được tạo có tên non-empty, sẽ thất bại và in "Trống".
uzsolt

4
ở đâu touch /emptytrong hàng của tôi?
Andrey Atapin

6
Ồ không! Có một vấn đề rất quan trọng với mã này. Cần phải if [ -n "$(ls -A /path/to/dir)" ]; then... Vui lòng cập nhật câu trả lời trước khi ai đó dán mã này vào máy chủ của họ ở đâu đó và một hacker tìm ra cách khai thác nó. Nếu /path/to/dirkhông trống, thì tên tệp ở đó được chuyển thành đối số /bin/testmà rõ ràng là không có ý định. Chỉ cần thêm -nđối số, vấn đề được giải quyết. Cảm ơn!
Edward Ned Harvey

20

Không cần phải đếm bất cứ điều gì hoặc vỏ quả cầu. Bạn cũng có thể sử dụng readkết hợp với find. Nếu findđầu ra trống, bạn sẽ quay lại false:

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

Điều này nên được xách tay.


Giải pháp tuyệt vời, nhưng tôi nghĩ rằng các echocuộc gọi của bạn phản ánh kết quả sai: trong thử nghiệm của tôi (theo Cygwin) find . -mindepth 1 | readcó mã lỗi 141 trong một thư mục không trống và 0 trong một thư mục trống
Lucas Cimon

@LucasCimon Không có ở đây (macOS và GNU / Linux). Đối với thư mục không trống, readtrả về 0 và cho thư mục trống, 1.
slhck

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

Đúng và nhanh. Tốt đẹp!
l0b0

3
Nó cần dấu ngoặc kép (2x) và kiểm tra -nlà chính xác và an toàn (kiểm tra với thư mục có khoảng trắng trong tên, kiểm tra nó với thư mục không trống có tên '0 = 1'). ... [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; ...
Zrin

1
@ivan_pozdeev Điều đó không đúng, ít nhất là đối với GNU find. Bạn có thể nghĩ về grep. serverfault.com/questions/225798/ Mạnh
Vladimir Panteleev

Nó có thể đơn giản hơn để viết find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty | grep .và dựa vào trạng thái thoát khỏi grep. Dù bạn làm theo cách nào thì đây cũng là câu trả lời đúng cho câu hỏi này.
Tom Anderson

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

4
Đó phải là nó, không cần phân tích đầu ra ls, chỉ cần xem nó có trống hay không. Sử dụng find chỉ cảm thấy như một quá mức đối với tôi.
akostadinov

4

Điều này sẽ thực hiện công việc trong thư mục làm việc hiện tại (.):

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

hoặc cùng một lệnh được phân chia trên ba dòng để dễ đọc hơn:

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

Chỉ cần thay thế ls -1A . | wc -lbằng ls -1A <target-directory> | wc -lnếu bạn cần chạy nó trên một thư mục đích khác.

Chỉnh sửa : Tôi đã thay thế -1abằng -1A(xem bình luận @Daniel)


2
sử dụng ls -Athay thế. Một số hệ thống tệp không có ...liên kết tượng trưng theo tài liệu.
Daniel Beck

1
Cảm ơn @Daniel, tôi đã chỉnh sửa câu trả lời của mình sau đề nghị của bạn. Tôi biết "1" cũng có thể bị xóa.
ztank1013

2
Nó không bị tổn thương, nhưng nó ngụ ý nếu đầu ra nó không đến một thiết bị đầu cuối. Vì bạn chuyển nó sang chương trình khác, nó không cần thiết.
Daniel Beck

-1chắc chắn là dư thừa. Ngay cả khi lssẽ không in một mục trên mỗi dòng khi nó sẽ được in thì nó cũng không ảnh hưởng đến ý tưởng kiểm tra xem nó có tạo ra 0 hay nhiều dòng không.
Victor Yarema

3

Sử dụng như sau:

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

Bằng cách này, bạn độc lập với định dạng đầu ra của ls. -mindepthbỏ qua chính thư mục, -maxdepthngăn chặn đệ quy vào các thư mục con để tăng tốc mọi thứ.


Tất nhiên, bây giờ bạn phụ thuộc vào wc -lfindđịnh dạng đầu ra (mặc dù khá đơn giản).
Daniel Beck

3

Sử dụng một mảng:

files=( * .* )
if (( ${#files[@]} == 2 )); then
    # contents of files array is (. ..)
    echo dir is empty
fi

3
Giải pháp rất hay, nhưng lưu ý rằng nó yêu cầushopt -s nullglob
xebeche

3
Các ${#files[@]} == 2giả thiết không đại diện cho các thư mục gốc (bạn có thể sẽ không kiểm tra nếu nó trống rỗng nhưng một số mã mà không biết về giới hạn mà có thể).
ivan_pozdeev

3

Một cách hacky, nhưng chỉ bash, không có PID:

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

Điều này lợi dụng thực tế là testnội dung dựng sẵn với 2 nếu được đưa ra nhiều hơn một đối số sau -e: Đầu tiên, toàn "$1"/*cầu được mở rộng bằng bash. Điều này dẫn đến một đối số cho mỗi tệp. Vì thế

  • Nếu không có tệp nào, dấu hoa thị test -e "$1"*không mở rộng, do đó Shell quay lại dùng thử tệp có tên *, trả về 1.

  • ... ngoại trừ nếu thực sự có một tệp có tên chính xác *, thì dấu hoa thị sẽ mở rộng thành tốt, dấu hoa thị, kết thúc như một cuộc gọi như trên, tức là. test -e "dir/*", chỉ lần này trả về 0. (Cảm ơn @TrueY đã chỉ ra điều này.)

  • Nếu có một tệp, test -e "dir/file"được chạy, trả về 0.

  • Nhưng nếu có nhiều tệp hơn 1, test -e "dir/file1" "dir/file2" được chạy, bash báo cáo đó là lỗi sử dụng, tức là 2.

case bao bọc toàn bộ logic xung quanh để chỉ trường hợp đầu tiên, với 1 trạng thái thoát được báo cáo là thành công.

Các vấn đề có thể tôi chưa kiểm tra:

  • Có nhiều tệp hơn số lượng đối số được phép - Tôi đoán điều này có thể hoạt động tương tự như trường hợp với hơn 2 tệp.

  • Hoặc thực sự có tệp có tên trống - Tôi không chắc nó có thể có trên bất kỳ HĐH / FS lành mạnh nào.


1
Chỉnh sửa nhỏ: nếu không có tệp nào trong dir /, thì test -e dir/*được gọi. Nếu tệp duy nhất là '*' trong thư mục thì kiểm tra sẽ trả về 0. Nếu có nhiều tệp hơn, thì nó trả về 2. Vì vậy, nó hoạt động như mô tả.
TrueY

Bạn nói đúng, @TrueY, tôi đã kết hợp nó trong câu trả lời. Cảm ơn!
Alois Mahdal

2

Với FIND (1) (trong Linux và FreeBSD), bạn có thể nhìn không đệ quy vào một mục nhập thư mục thông qua "-maxdepth 0" và kiểm tra xem nó có trống với "-empty" không. Áp dụng cho câu hỏi này:

if test -n "$(find ./ -maxdepth 0 -empty)" ; then
    echo "No new file"
    exit 1
fi

Nó có thể không di động 100%, nhưng nó thanh lịch.
Craig Ringer

1

Tôi nghĩ giải pháp tốt nhất là:

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

cảm ơn https://stackoverflow.com/a/91558/520567

Đây là một chỉnh sửa ẩn danh cho câu trả lời của tôi có thể có hoặc không hữu ích với ai đó: Một thay đổi nhỏ cho số lượng tệp:

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

Điều này sẽ hoạt động chính xác ngay cả khi tên tệp chứa khoảng trắng.


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

EDIT: Tôi nghĩ rằng giải pháp này hoạt động tốt với gnu find, sau khi xem nhanh việc thực hiện . Nhưng điều này có thể không hoạt động với, ví dụ, tìm kiếm của netbsd . Thật vậy, cái đó sử dụng trường st_size của stat (2). Hướng dẫn mô tả nó như sau:

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.

Một giải pháp tốt hơn, cũng đơn giản hơn, là:

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

Ngoài ra, -prune trong giải pháp 1 là vô ích.

EDIT: không -exit cho gnu find .. giải pháp trên là tốt cho tìm kiếm của NetBSD. Đối với GNU find, điều này sẽ hoạt động:

if [ -z "`find \"${DIR}\" -mindepth 1 -exec echo notempty \; -quit`" ]; then
    echo Empty
else
    echo Not Empty
fi

tìm từ GNU findutils 4.6.0 (phiên bản mới nhất) không có -exitvị ngữ .
Dennis

0

Đây là tất cả những thứ tuyệt vời - chỉ cần biến nó thành một kịch bản để tôi có thể kiểm tra các thư mục trống bên dưới thư mục hiện tại. Phần bên dưới nên được đặt vào một tệp có tên 'findempty', được đặt trong đường dẫn ở đâu đó để bash có thể tìm thấy nó và sau đó chmod 755 để chạy. Tôi có thể dễ dàng sửa đổi theo nhu cầu cụ thể của bạn, tôi đoán.

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

0

Công việc này đối với tôi, để kiểm tra & xử lý tệp trong thư mục ../IN, xem xét tập lệnh nằm trong ../Scriptthư mục:

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

0

Điều gì về kiểm tra nếu thư mục tồn tại và không trống trong một câu lệnh if

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

-1

Đối với bất kỳ thư mục nào khác ngoài thư mục hiện tại, bạn có thể kiểm tra xem nó có trống không bằng cách thử rmdirnó, bởi vì rmdirđược đảm bảo không thành công cho các thư mục không trống. Nếu rmdirthành công và bạn thực sự muốn thư mục trống tồn tại trong bài kiểm tra, chỉ cần mkdirmột lần nữa.

Đừng sử dụng bản hack này nếu có các quy trình khác có thể bị biến dạng bởi một thư mục mà họ biết về việc ngừng tồn tại trong một thời gian ngắn.

Nếu rmdirbạn không làm việc cho bạn và bạn có thể đang kiểm tra các thư mục có khả năng chứa số lượng lớn tệp, bất kỳ giải pháp nào dựa vào shell shellbing đều có thể bị chậm và / hoặc chạy trong giới hạn độ dài dòng lệnh. Có lẽ tốt hơn để sử dụng findtrong trường hợp đó. findGiải pháp nhanh nhất tôi có thể nghĩ ra

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

Điều này hoạt động cho các phiên bản GNU và BSD findnhưng không phải cho phiên bản Solaris, vốn thiếu mỗi một findtoán tử. Yêu công việc của bạn, Oracle.


Không phải là một ý tưởng tốt. OP chỉ đơn giản muốn kiểm tra xem thư mục có trống hay không.
roaima

-3

Bạn có thể thử xóa thư mục và chờ lỗi; rmdir sẽ không xóa thư mục nếu nó không trống.

_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

3
-1 Đây là loại mã phản tác dụng. rmdirsẽ thất bại nếu tôi không có quyền xóa thư mục; hoặc nếu đó là một subvolume Btrfs; hoặc nếu nó thuộc về một hệ thống tập tin chỉ đọc. Và nếu rmdirkhông thất bại và mkdirchạy: nếu thư mục đã bị xóa thuộc về người dùng khác thì sao? những gì về quyền (có thể không chuẩn) của nó? ACL? thuộc tính mở rộng? Tất cả đã mất.
Kamil Maciorowski

Chà, tôi chỉ đang học bash và tôi nghĩ rằng nó có thể là một cách nhanh hơn thay vì lặp qua tất cả các thư mục, nhưng CPU rất mạnh và bạn đúng, không an toàn.
impxd
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.