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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6916559/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 20:49:41  来源:igfitidea点击:

Test multiple file conditions in one swoop (BASH)?

bashtestingscriptingsyntax

提问by Alex Gray

Often when writing for the bash shell, one needs to test if a file (or Directory) exists (or doesn't exist) and take appropriate action. Most common amongst these test are...

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

-e- file exists, -f- file is a regular file (not a directory or device file), -s- file is not zero size, -d- file is a directory, -r- file has read permission, -w- file has write, or -xexecute permission (for the user running the test)

-e- 文件存在,-f- 文件是一个普通文件(不是目录或设备文件),-s- 文件不是零大小,-d- 文件是一个目录,-r- 文件有读权限,-w- 文件有写或-x执行权限(对于用户运行测试)

This is easily confirmed as demonstrated on this user-writable directory....

正如此用户可写目录中所示,这很容易确认....

#/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 you ?
? YES SIR -w is fine ?
? YES SIR -d is fine ?

? 没有 -f 吗?
? 是的,先生 -w 很好吗?
? 是的 SIR -d 可以吗?

My question, although seemingly obvious, and unlikely to be impossible - is how to simply combinethese tests, without having to perform them separately for each condition... Unfortunately...

我的问题虽然看起来很明显,也不太可能是不可能的 - 是如何简单地组合这些测试,而不必为每个条件单独执行它们......不幸的是......

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 ?
? no [ -w [ -d .. ]] for you ?
? no -w && -d for you ?

? 没有 -wd 吗?
? 没有 -w | -d 给你?
? 没有 [ -w [ -d .. ]] 给你?
? 没有 -w && -d 吗?

What am I missing here?

我在这里缺少什么?

回答by Kerrek SB

You can use logical operators to multiple conditions, e.g. -afor AND:

您可以使用逻辑运算符将多个条件,例如-aAND

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

回答by Micha? ?rajer

Of course, you need to use AND somehow as Kerrek(+1) and Ben(+1) pointed it out. You can do in in few different ways. Here is an ala-microbenchmark results for few methods:

当然,正如 Kerrek(+1) 和 Ben(+1) 指出的那样,您需要以某种方式使用 AND。您可以通过几种不同的方式进行。以下是几种方法的 ala-microbenchmark 结果:

Most portable and readable way:

最便携和可读的方式:

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

still portable, less readable, faster:

仍然便携,可读性差,速度更快:

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

bashism, but readable and faster

bashism,但可读性更强,速度更快

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

bashism, but quite readable, and fastest.

bashism,但相当可读,而且速度最快。

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

Note, that in bash, "[" is a builtin, so bash is using internal command not a symlink to /usr/bin/test exacutable. The "[[" is a bash keyword. So the slowest possible way will be:

请注意,在 bash 中,“[”是一个内置命令,因此 bash 使用的是内部命令,而不是指向 /usr/bin/test 可执行文件的符号链接。"[[" 是一个 bash 关键字。所以最慢的方法是:

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

回答by Ben Hymanson

You want -aas in -f foo -a -d foo(actually that test would be false, but you get the idea).

你想-a-f foo -a -d foo(实际上是测试是假的,但你的想法)。

You were close with &you just needed &&as in [ -f foo ] && [ -d foo ]although that runs multiple commands rather than one.

尽管它运行多个命令而不是一个命令,但您与&您的需求很接近。&&[ -f foo ] && [ -d foo ]

Here is a manual page for testwhich is the command that [is a link to. Modern implementations of testhave a lot more features (along with the shell-builtin version [[which is documented in your shell's manpage).

这是一个用于测试的手册页,[是一个链接到的命令。的现代实现test具有更多功能(以及[[shell 联机帮助页中记录的 shell 内置版本)。

回答by jonretting

check-file(){
    while [[ ${#} -gt 0 ]]; do
        case  in
           fxrsw) [[ -f "" && -x "" && -r "" && -s "" && -w "" ]] || return 1 ;;
            fxrs) [[ -f "" && -x "" && -r "" && -s "" ]] || return 1 ;;
             fxr) [[ -f "" && -x "" && -r "" ]] || return 1 ;;
              fr) [[ -f "" && -r "" ]] || return 1 ;;
              fx) [[ -f "" && -x "" ]] || return 1 ;;
              fe) [[ -f "" && -e "" ]] || return 1 ;;
              hf) [[ -h "" && -f "" ]] || return 1 ;;
               *) [[ -e "" ]] || 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

回答by chad

Why not write a function to do it?

为什么不写一个函数来做呢?

check_file () {
    local FLAGS=
    local PATH=
    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
}

Then just use it like:

然后像这样使用它:

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
}

And you should get:

你应该得到:

/ 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

回答by Gao

This seems to work (notice the double brackets):

这似乎有效(注意双括号):

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