2011-08-02 34 views
31

通常在爲bash shell編寫代碼時,需要測試一個文件(或目錄)是否存在(或不存在)並採取適當的操作。最常見的在這些測試都是......一次性測試多個文件條件(BASH)?

-e - 文件存在,-f - 文件是一個普通文件(不是目錄或者設備文件),-s - 文件不是零大小,-d - 文件是目錄,-r - 文件讀取權限,-w - 文件有寫,或-x執行權限(用於運行測試的用戶)

用戶可寫目錄 ....

#/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 
作爲證明這很容易證實

➝沒有-f你✓
➝YES SIR -w是罰款✓
➝YES SIR -d是罰款✓

我的問題,雖然看似明顯,而不太可能是不可能的 - 是如何簡單結合這些測試中,無需單獨執行它們每個條件。可惜......

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 `]' 

➝沒有-wd你✖
➝no -w | -d爲您✖
➝沒有[-w [-d ..]你✖
➝沒有-w & & -d你✖

缺少什麼我在這裏?

回答

7

你想-a-f foo -a -d foo(實際上,測試將是錯誤的,但你明白了)。

您近距離接觸&您只需要&&[ -f foo ] && [ -d foo ]儘管它運行多個命令而不是一個命令。

這是a manual page for test這是[是鏈接到的命令。 test的現代實現具有更多的功能(以及shell-built版本[[,該文件記錄在shell的聯機幫助頁中)。

+1

根據[這些人](http://tldp.org/LDP/abs/html/fto.html)...「'-a'文件存在 - 這與-e效果相同。 「已棄用」,其使用是不鼓勵的。「不贊成使用這個腳註... ...根據1913年韋伯斯特詞典的版本: 棄用 ... 作爲一個邪惡祈禱,反對; 以避免祈禱; 欲刪除; 尋求釋放; 表示深表遺憾; 強烈反對 –

+0

@亞歷克斯:這是別的東西,不用擔心,現在'-a 'means「和」。 –

32

您可以對多個條件使用邏輯運算符,例如-aAND

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

需要兩次寫入路徑,這真的有必要嗎? –

+1

這就是爲什麼我使用一個變量來減少重複的原因,如果你願意,可以縮短它的長度,只要注意不要與已經存在。 –

21

當然,你需要使用並以某種方式爲Kerrek(+1)和Ben(+1)指出了這一點。你可以用幾種不同的方式做。下面是幾種方法的ALA-微基準測試結果:

大多數便攜式可讀的方式:

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

還是便攜,可讀性變差,速度快:

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

bashism,但可讀性和更快

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

bashism,但很可讀,最快。

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

注意,在bash中, 「[」 是一個內置的,所以bash的是使用內部命令不是一個符號鏈接到/ usr /斌/測試exacutable。 「[[」是一個bash關鍵字。所以最慢的方法是:

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

很好的評價。我很震驚,[['實際上比'['快。任何想法爲什麼? – bitmask

+3

@bitmask:可能是因爲它是內置的Bash構造,而不是OS命令。 – l0b0

+0

@ l0b0:'''這裏!正如上面Michael所指出的那樣。 – bitmask

2

爲什麼不寫一個函數來做到這一點?

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 
} 

然後,只需使用它像:

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 
} 

,你應該得到:

/ 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

不推薦使用[[-f「$ file」-a -x「$ file]]。應該使用[[-f」$ file「]] && [[-d」$ file「]]或[[ -f「$ file」&& -x「$ file]]代替。此外還有很多冗餘,例如對-e和-x進行首次測試是多餘的,因爲如果文件不存在,-x將返回false。對於許多其他組合也是如此。我發佈的例子並不意味着從字面上理解,而是一個指南。 – jonretting

2

這似乎是工作(注意雙括號內):

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

這不適用於我的外殼: Linux 2.6.32-431.29.2.el6.x86_64#1 SMP x86_64 x86_64 x86_64 GNU/Linux – Florian

+0

@Florian所以顯然我給了一個無效的答案。請參閱:https://unix.stackexchange.com/questions/413168/test-multiple-file-conditions-by-combining-flags-does-it-work。 – Gao