2011-08-02 26 views
31

Thường khi viết cho bash shell, người ta cần kiểm tra xem một tệp (hoặc Thư mục) tồn tại (hoặc không tồn tại) và thực hiện hành động thích hợp. Phổ biến nhất trong số những thử nghiệm là ...Kiểm tra nhiều điều kiện tệp trong một lần đổi (BASH)?

-e - tập tin tồn tại, -f - file là một file bình thường (không phải là một thư mục hoặc thiết bị tập tin), -s - tập tin không phải là kích thước không, -d - tập tin là một thư mục, -r cho phép tập tin đã đọc, -w - - tập tin có viết, hoặc -x quyền thực thi (đối với người dùng chạy thử nghiệm)

này có thể dễ dàng xác nhận là chứng minh trên thư mục người dùng có thể ghi này ....

#/bin/bash 

if [ -f "/Library/Application Support" ]; then 
echo 'YES SIR -f is fine' 
else echo 'no -f for you' 
fi 

if [ -w "/Library/Application Support" ]; then 
echo 'YES SIR -w is fine' 
else echo 'no -w for you' 
fi 

if [ -d "/Library/Application Support" ]; then 
echo 'YES SIR -d is fine' 
else echo 'no -d for you' 
fi 

➝ không -f cho bạn ✓
➝ YES SIR -w là tốt ✓
➝ YES SIR -d là tốt ✓

Câu hỏi của tôi, mặc dù có vẻ hiển nhiên, và khó có khả năng là không thể - là làm thế nào để đơn giản kết hợp những thử nghiệm này, mà không cần phải thực hiện một cách riêng biệt cho từng tình trạng ... Thật không may ...

if [ -wd "/Library/Application Support" ] 
    ▶ -wd: unary operator expected 

if [ -w | -d "/Library/Application Support" ] 
    ▶ [: missing `]' 
    ▶ -d: command not found 

if [ -w [ -d "/Library.... ]] & if [ -w && -d "/Library.... ] 
    ▶ [: missing `]' 

➝ không -wd cho bạn ✖
➝ no -w | -d cho bạn ✖
➝ không [-w [-d ..]] cho bạn ✖
➝ không -w & & -d cho bạn ✖

tôi thiếu gì ở đây?

Trả lời

7

Bạn muốn -a như trong -f foo -a -d foo (thực sự thử nghiệm đó là sai, nhưng bạn có ý tưởng).

Bạn đã đóng với & bạn chỉ cần && như trong [ -f foo ] && [ -d foo ] mặc dù chạy nhiều lệnh thay vì một lệnh.

Đây là a manual page for test là lệnh mà [ là liên kết đến. Triển khai hiện đại của test có nhiều tính năng hơn (cùng với phiên bản shell-builtin [[ được ghi lại trong manpage của trình bao của bạn).

+1

Theo [những kẻ này] (http://tldp.org/LDP/abs/html/fto.html) ... "' -a' tập tin tồn tại - Điều này giống hệt với hiệu ứng -e. "Không được chấp nhận", và việc sử dụng nó không được khuyến khích. "Không được chấp nhận sau đó được chú thích với điều này ... Mỗi ấn bản năm 1913 của Từ điển Webster: Không được chấp nhận ... Để cầu nguyện, như một điều ác; để tìm kiếm để thay đổi bằng cách cầu nguyện, để mong muốn xóa bỏ; để tìm kiếm sự giải thoát khỏi; để bày tỏ sự hối tiếc sâu sắc cho; để từ chối mạnh mẽ –

+0

@ Alex: Đó là điều gì đó khác, đừng lo lắng về điều đó. "có nghĩa là" và " –

32

Bạn có thể sử dụng toán tử logic cho nhiều điều kiện, ví dụ: -a cho AND:

MYFILE=/tmp/data.bin 
if [ -f "$MYFILE" -a -r "$MYFILE" -a -w "$MYFILE" ]; then 
    #do stuff 
fi 
unset MYFILE 
+1

đòi hỏi phải viết đường dẫn hai lần. Nó có thực sự cần thiết không? –

+1

Đó là lý do tại sao tôi sử dụng một biến để giảm trùng lặp. Bạn có thể làm cho nó ngắn hơn nếu bạn thích, chỉ cần cẩn thận để không đụng độ với bất cứ thứ gì tồn tại. –

21

Tất nhiên, bạn cần phải sử dụng AND bằng cách nào đó như Kerrek (+1) và Ben (1) chỉ ra. Bạn có thể làm theo một vài cách khác nhau.Dưới đây là một kết quả ala-microbenchmark cho vài phương pháp:

di động và có thể đọc được cách nhất:

$ time for i in $(seq 100000); do [ 1 = 1 ] && [ 2 = 2 ] && [ 3 = 3 ]; done 
real 0m2.583s 

vẫn di động, ít có thể đọc được, nhanh hơn:

$ time for i in $(seq 100000); do [ 1 = 1 -a 2 = 2 -a 3 = 3 ]; done 
real 0m1.681s 

bashism, nhưng có thể đọc được và nhanh hơn

$ time for i in $(seq 100000); do [[ 1 = 1 ]] && [[ 2 = 2 ]] && [[ 3 = 3 ]]; done 
real 0m1.285s 

bashism, nhưng khá dễ đọc và nhanh nhất.

$ time for i in $(seq 100000); do [[ 1 = 1 && 2 = 2 && 3 = 3 ]]; done 
real 0m0.934s 

Lưu ý rằng trong bash, "[" được gắn sẵn, vì vậy bash đang sử dụng lệnh nội bộ không phải là liên kết tượng trưng đến/usr/bin/test exacutable. "[[" Là từ khóa bash. Vì vậy, cách chậm nhất có thể sẽ là:

time for i in $(seq 100000); do /usr/bin/\[ 1 = 1 ] && /usr/bin/\[ 2 = 2 ] && /usr/bin/\[ 3 = 3 ]; done 
real 14m8.678s 
+2

Đánh giá đẹp Tôi đã bị sốc rằng '[['thực sự nhanh hơn' ['. Bất kỳ ý tưởng tại sao? – bitmask

+3

@bitmask: Có lẽ vì nó là một xây dựng trong xây dựng Bash, chứ không phải là một lệnh hệ điều hành. – l0b0

+0

@ l0b0: Vậy là '[' ở đây! Như đã lưu ý của Michael ở trên. – bitmask

2

Tại sao không viết chức năng để làm điều đó?

check_file() { 
    local FLAGS=$1 
    local PATH=$2 
    if [ -z "$PATH" ] ; then 
     if [ -z "$FLAGS" ] ; then 
      echo "check_file: must specify at least a path" >&2 
      exit 1 
     fi 
     PATH=$FLAGS 
     FLAGS=-e 
    fi 
    FLAGS=${FLAGS#-} 
    while [ -n "$FLAGS" ] ; do 
     local FLAG=`printf "%c" "$FLAGS"` 
     if [ ! -$FLAG $PATH ] ; then false; return; fi 
     FLAGS=${FLAGS#?} 
    done 
    true 
} 

Sau đó chỉ cần sử dụng nó như:

for path in//etc /etc/passwd /bin/bash 
{ 
    if check_file -dx $path ; then 
     echo "$path is a directory and executable" 
    else 
     echo "$path is not a directory or not executable" 
    fi 
} 

Và bạn sẽ nhận được:

/ is a directory and executable 
/etc is a directory and executable 
/etc/passwd is not a directory or not executable 
/bin/bash is not a directory or not executable 
3
check-file(){ 
    while [[ ${#} -gt 0 ]]; do 
     case $1 in 
      fxrsw) [[ -f "$2" && -x "$2" && -r "$2" && -s "$2" && -w "$2" ]] || return 1 ;; 
      fxrs) [[ -f "$2" && -x "$2" && -r "$2" && -s "$2" ]] || return 1 ;; 
      fxr) [[ -f "$2" && -x "$2" && -r "$2" ]] || return 1 ;; 
       fr) [[ -f "$2" && -r "$2" ]] || return 1 ;; 
       fx) [[ -f "$2" && -x "$2" ]] || return 1 ;; 
       fe) [[ -f "$2" && -e "$2" ]] || return 1 ;; 
       hf) [[ -h "$2" && -f "$2" ]] || return 1 ;; 
       *) [[ -e "$1" ]] || return 1 ;; 
     esac 
     shift 
    done 
} 

check-file fxr "/path/file" && echo "is valid" 

check-file hf "/path/folder/symlink" || { echo "Fatal error cant validate symlink"; exit 1; } 

check-file fe "file.txt" || touch "file.txt" && ln -s "${HOME}/file.txt" "/docs/file.txt" && check-file hf "/docs/file.txt" || exit 1 

if check-file fxrsw "${HOME}"; then 
    echo "Your home is your home from the looks of it." 
else 
    echo "You infected your own home." 
fi 
+2

Bạn không nên sử dụng [[-f "$ file" -a -x "$ file]. Bạn nên sử dụng [[-f" $ file "]] && [[-d" $ file "]] hoặc [[ -f "$ file" && -x "$ file]] thay vào đó. Ngoài ra có rất nhiều dự phòng ở đây ví dụ thử nghiệm đầu tiên cho -e và sau đó -x là dự phòng, bởi vì -x sẽ trả về false nếu tệp không tồn tại. Điều tương tự cho nhiều kết hợp khác. Ví dụ tôi đăng không có nghĩa là được thực hiện theo nghĩa đen, mà là một hướng dẫn. – jonretting

2

Điều này dường như làm việc (chú ý dấu ngoặc kép):

#!/bin/bash 

if [[ -fwd "/Library/Application Support" ]] 
then 
    echo 'YES SIR -f -w -d are fine' 
else 
    echo 'no -f or -w or -d for you' 
fi 
+2

Điều này không hoạt động trên vỏ của tôi: Linux 2.6.32-431.29.2.el6.x86_64 # 1 SMP x86_64 x86_64 x86_64 GNU/Linux – Florian

+0

@Florian Vì vậy, dường như tôi đã đưa ra một câu trả lời không hợp lệ. Xem: https://unix.stackexchange.com/questions/413168/test-multiple-file-conditions-by-combining-flags-does-it-work. – Gao

Các vấn đề liên quan