【问题标题】:Test multiple file conditions in one swoop (BASH)?一口气测试多个文件条件(BASH)?
【发布时间】:2011-10-18 11:54:19
【问题描述】:

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

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

➝ 不适合你 ✖
➝ 不-w | -d 为你 ✖
➝ 没有 [ -w [ -d .. ]] 给你 ✖
➝ no -w && -d for you ✖

我在这里缺少什么?

【问题讨论】:

    标签: bash testing scripting syntax


    【解决方案1】:

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

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

    【讨论】:

    • 需要两次写入路径。真的有必要吗?
    • 这就是我使用变量来减少重复的原因。如果你愿意,你可以让它更短,注意不要与任何已经存在的东西发生冲突。
    • -a 不再是 POSIX 了,这似乎是值得注意的。不要指望它适用于所有外壳。 (我至少在 Debian Sid 中查看过man 1posix test)。
    【解决方案2】:

    当然,正如Kerrek(+1) 和Ben(+1) 指出的那样,您需要以某种方式使用AND。您可以通过几种不同的方式进行操作。以下是几种方法的 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
    

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

    【讨论】:

    • 不错的评价。我很震惊[[ 实际上比[ 快。知道为什么吗?
    • @bitmask:可能是因为它是内置的 Bash 结构,而不是操作系统命令。
    • @l0b0:[ 也在这里!正如上面迈克尔所指出的那样。
    • @l0b0: 当然有一个合适的test/[ 程序位于/usr/bin/ 中,但这并不能阻止bash 使用它自己的内置实现[ .首先在man testman bash/man builtins中注明。其次,如果您查看基准测试结果,如果您明确使用 /usr/bin/[ 而不是 [(约 13.8 分钟与约 2.5 秒),则会有明显的损失。
    • @l0b0 您应该使用type 而不是which 来检查shell 如何查看命令以及将执行什么。 type [: [ is a shell builtin and type [[: [[ is a shell keyword Command which 只定位一个命令,对shell一无所知内置。
    【解决方案3】:

    你想要-a 就像-f foo -a -d foo 一样(实际上那个测试是错误的,但你明白了)。

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

    这里是a manual page for test,这是[ 链接的命令。 test 的现代实现具有更多功能(以及 shell 的手册页中记录的 shell 内置版本 [[)。

    【讨论】:

    • 根据these guys... “-a 文件存在 - 这与 -e 效果相同。它已被“弃用”,不鼓励使用。 Deprecated 然后用这个作为脚注...根据 1913 年版的韦伯斯特词典:Deprecate ... 祈祷反对,作为一种邪恶;寻求通过祈祷来避免;希望移除;寻求解脱;表示深深的遗憾;强烈反对。
    • @Alex:那是另外一回事,不用担心。现在-a 表示“和”。
    【解决方案4】:
    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
    

    【讨论】:

    • 不推荐使用 [[ -f "$file" -a -x "$file ]]。应该使用 [[ -f "$file" ]] && [[ -d "$ file" ]] 或 [[ -f "$file" && -x "$file ]] 代替。这里也有很多冗余,例如首先测试 -e 然后 -x 是冗余的,因为如果文件不存在, -x 将返回 false 。许多其他组合也是如此。我发布的示例不是字面意思,而是一个指南。
    【解决方案5】:

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

    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
    

    【讨论】:

      【解决方案6】:

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

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

      【讨论】:

      • 这在我的 shell 上不起作用:Linux 2.6.32-431.29.2.el6.x86_64 #1 SMP x86_64 x86_64 x86_64 GNU/Linux
      • @Florian 所以显然我给出了一个无效的答案。请参阅:unix.stackexchange.com/questions/413168/…
      猜你喜欢
      • 2015-06-13
      • 1970-01-01
      • 2012-11-02
      • 2020-05-25
      • 2013-09-29
      • 1970-01-01
      • 2018-09-13
      • 2011-07-26
      • 1970-01-01
      相关资源
      最近更新 更多