Tại sao kiểm tra xem một thư mục tồn tại bằng cách sử dụng -d trên một chuỗi trống có trả về đúng không?


8

Tôi đã viết một số kịch bản và viết một cái gì đó như

ARTIFACTS="/SOME/PATH"
[ -d $ARTIFCATS ] && rm -rf $ARTIFACTS/*

Điều xảy ra là vì sự ngu ngốc, tôi đã thực hiện dòng thứ hai mà không thực hiện dòng đầu tiên. Hóa ra [-d ​​""] trả về đúng và biểu thức trở thành

rm -rf /*

May mắn thay, nó chỉ là một máy thử nghiệm và tôi không phải là sudo, nhưng mặc dù tôi đã mất một số dữ liệu

Câu hỏi của tôi là, tại sao [-d ""] trở lại đúng ?? tài liệu nêu rõ nó kiểm tra xem một đường dẫn có tồn tại hay không và là một thư mục

Tôi đã giải quyết vấn đề bằng cách sử dụng

[ -e $ARTIFACTS ]
có vẻ như để làm việc

Chúc mừng


5
Hoặc có thể bạn đã thực hiện cả hai dòng. Trong ví dụ mã ở trên, bạn không bao giờ đặt ARTIFCATS.
Buhb

2
Tôi sẽ chỉ viết mà rm -rf $ARTIFACTSkhông có /*. Điều này cũng sẽ xóa $ARTIFACTSthư mục, điều này là tốt, bởi vì nếu tôi muốn chắc chắn rằng nó tồn tại trước khi đặt một cái gì đó vào nó, tôi sẽ thực hiện bằng mkdir -p $ARTIFACTSmọi cách. Nó cũng sẽ xóa các tập tin ẩn bên trong $ARTIFACTS, điều này cũng tốt, vì tôi sẽ không viết rm -rf $ARTIFACTS/*nếu $ARTIFACTSchứa bất cứ thứ gì tôi muốn lưu.
Christoffer Hammarström

@ Christoffer
Hammarström

Câu trả lời:


9

1. Hai bài kiểm tra này trả về đúng :

# [ -d ] && echo true || echo false
true
# [ -d $SOME_UNSET_VAR ] && echo true || echo false
true

theo POSIX (như được giải thích bởi @Tim).

2. Nhưng điều này trả về sai ( không đúng như đã nêu trong câu hỏi)

# [ -d "" ] && echo true || echo false
false

bởi vì testđược gọi với hai đối số (mặc dù đối số thứ hai là một chuỗi rỗng).

3. Đó là lý do tại sao nên sử dụng [[ … ]]thay vì test( [ … ]), mà hầu hết (tất cả?) Vỏ hiện tại cung cấp. Cấu trúc này kiểm tra nếu bạn cung cấp đủ đối số (nếu không sẽ ném lỗi và hủy bỏ)

# [[ -d ]] && echo true || echo false
bash: unexpected argument `]]' to conditional unary operator
bash: syntax error near `]]'

hoặc đơn giản là hành xử như người ta mong đợi:

# [[ -d $SOME_UNSET_VAR ]] && echo true || echo false
false

4. Và, như được chỉ ra bởi @Gilles, thậm chí quan trọng hơn là tăng gấp đôi thay thế. Vì vậy, -d "$SOME_UNSET_VAR"mở rộng đến -d ""và trả về false ngay cả với test(bằng trường hợp 2). Do đó, điều này cũng tương thích với vỏ Bourne sh:

# [ -d "$SOME_UNSET_VAR" ] && echo true || echo false
false

đã thử nghiệm với bash 3.00.16 (1) và 4.1.5 (1)


1
Bash, ksh và zsh cung cấp [[ … ]]nhưng không đơn giản sh. Thực tiễn thực sự quan trọng là đặt dấu ngoặc kép xung quanh thay thế lệnh : [ -d "$ARTIFACTS" ].
Gilles 'SO- ngừng trở nên xấu xa'

6

Câu hỏi này đã được trả lời trên StackOverflow. Nó nói rằng theo tiêu chuẩn POSIX, testphải luôn trả về thành công nếu nó được gọi với chính xác một đối số không trống (và không có đối số nào khác).

Đây cũng là trường hợp với test -e(và trên thực tế nó nằm trong hệ thống của tôi), vì vậy hãy cẩn thận.

Thay vào đó sử dụng:

[ -d "$ARTIFACTS" ]

test sau đó sẽ được gọi với hai đối số ngay cả khi biến đó trống và trả về false trong trường hợp này.


"chính xác là một đối số không trống rỗng." - đây là một bản tóm tắt sai lệch - trang bạn liên kết đề cập đến được gọi với chính xác một đối số và đối số đó không trống; không có gì về trường hợp của một cuộc tranh cãi không có người theo sau là một cuộc tranh luận trống rỗng. Giải pháp chính xác là bao quanh tên biến trong dấu ngoặc kép.
Random832

@ Random832 Cảm ơn bạn! Tôi thực hiện cả hai thay đổi được đề xuất của bạn.
Tim

[ ! -z $ARTIFACTS ] && [ -d $ARTIFACTS ]không tốt hơn: nó chỉ phục vụ cho trường hợp cụ thể khi $ARTIFACTStrống, nhưng không thành công khi $ARTIFACTScó thể chứa khoảng trắng hoặc \[?*. [ -d "$ARTIFACTS" ]là cách chính xác để làm điều đó (hoặc [[ -d $ARTIFACTS ]]trong vỏ có [[ … ]]).
Gilles 'SO- ngừng trở nên xấu xa'

@Gilles Bạn nói đúng, tôi đã xóa nó. Cảm ơn bạn!
Tim

4

Tôi đã giải quyết vấn đề bằng cách sử dụng [-e $ ARTIFACTS] có vẻ hiệu quả

Bạn đã sai . Nó hoạt động bởi vì $ARTIFACTSbây giờ được đặt thành một cái gì đó.

Khi một biến không được đặt, sau đó nói

[ -d $SOMEVAR ]

hoặc là

[ -e $SOMEVAR ]

cả hai sẽ đánh giá truebởi vì nó ngụ ý nói

[ -d ]

[ -e ]

tương ứng. (Nói [ foobar ]sẽ luôn luôn đánh giá là đúng.)

Nói

set -u

có ích trong các tình huống như vậy. help setsẽ nói với bạn:

  -u  Treat unset variables as an error when substituting.

[-e ""] && echo "IN IN SOMETHING" không in bất cứ điều gì, vậy làm thế nào điều này có thể sai?
Moataz Elmasry

2
ahh crap [-e $ ARTIFACTS] với các tạo tác trống rỗng mang lại [-e] không [-e ""], hiểu rồi
Moataz Elmasry

4

Xem rằng bạn đã đặt biến ARTIFACTS và bạn đang kiểm tra ARTIFCATS. Có lẽ là nhầm lẫn?

Dù sao, -d cũng như -e sẽ tạo ra kết quả tương tự trên các biến không đặt.

Do đó sử dụng dấu ngoặc kép và nó sẽ giúp bạn.

ARTIFACTS="/SOME/PATH"
[ -d "$ARTIFACTS" ] && rm -rf -- "$ARTIFACTS/"*

LƯU Ý: Nếu "/ MỘT SỐ / PATH" của bạn có bất kỳ thư mục nào có dung lượng trống, tập lệnh bạn đã đề cập sẽ bị hỏng với lỗi "toán tử nhị phân dự kiến".

Thí dụ:

ARTIFACTS="/home backup/"

1) [ -d $ARTIFACTS ] && rm -rf $ARTIFACTS/*
bash: [: /home: binary operator expected

2) [ -d "$ARTIFACTS" ] && rm -rf "$ARTIFACTS"/*

sẽ làm tốt Đừng quên đặt dấu ngoặc kép trong rmlời mời ( rm -rf $ARTIFACTSsẽ vui vẻ xóa /homesau đó phàn nàn về việc backup/*không tồn tại).

Ngoài ra, bao gồm -Lkiểm tra sẽ đảm bảo rằng đó là một thư mục chứ không chỉ là một liên kết tượng trưng đến một thư mục.

Nên về cơ bản,

[ -d "$ARTIFACTS" && ! -L "$ARTIFACTS" ] && rm -rf -- "$ARTIFACTS"/*
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.