一举测试多个文件条件(BASH)?

时间:2011-08-02 18:10:11

标签: bash testing scripting syntax

通常在编写bash shell时,需要测试文件(或目录)是否存在(或不存在)并采取适当的措施。这些测试中最常见的是......

-e - 文件存在,-f - 文件是常规文件(不是目录或设备文件),-s - 文件不是零大小,-d - file是一个目录,-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

➝no-f for✓✓
➝YESSIR -w很好✓
➝YESSIR -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 `]'

➝no-wd for you✖
➝no-w | -d for you✖
➝没有[-w [-d ..]]为你✖
➝no-w&& -d for you✖

我在这里缺少什么?

6 个答案:

答案 0 :(得分:33)

您可以将逻辑运算符用于多个条件,例如: -a的{​​{1}}:

AND

答案 1 :(得分:21)

当然,你需要以某种方式使用AND,因为Kerrek(+1)和Ben(+1)指出了它。你可以用几种不同的方式做。以下是几种方法的ala-microbenchmark结果:

最便携和可读的方式:

$ 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

基础,但可读且更快

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

基础,但非常可读,而且速度最快。

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

注意,在bash中,“[”是内置的,所以bash使用内部命令而不是符号链接到/ usr / bin / test 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 :(得分:8)

您希望-a-f foo -a -d foo一样(实际上该测试会失败,但您明白了。)

您与&关系密切,&&[ -f foo ] && [ -d foo ]一样,尽管它运行多个命令而不是一个命令。

以下是a manual page for test[是指向该链接的命令。 test的现代实现具有更多功能(以及shell内置版本[[,在shell的联机帮助页中有记录)。

答案 3 :(得分: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

答案 4 :(得分: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

答案 5 :(得分: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