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

Ale*_*ray 35 testing syntax bash scripting

通常在为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
Run Code Online (Sandbox Code Playgroud)

➝没有-f为你
✓➝是SIR -w很好
✓➝是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 `]'
Run Code Online (Sandbox Code Playgroud)

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

我在这里错过了什么?

Ker*_* SB 36

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

MYFILE=/tmp/data.bin
if [ -f "$MYFILE"  -a  -r "$MYFILE"  -a  -w "$MYFILE" ]; then
    #do stuff
fi
unset MYFILE
Run Code Online (Sandbox Code Playgroud)

  • 这需要写两次路径.真的有必要吗? (2认同)
  • 这就是我使用变量来减少重复的原因.如果你愿意,你可以让它更短,只要注意不要与已经存在的任何东西发生冲突. (2认同)

Mic*_*jer 22

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

最便携和可读的方式:

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

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

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

卑鄙,但可读性和更快

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

卑鄙,但相当可读,最快.

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

注意,在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
Run Code Online (Sandbox Code Playgroud)

  • @bitmask:可能是因为它是内置的Bash结构,而不是OS命令. (3认同)
  • @ l0b0:当然有一个正确的`test` /`[`程序位于`/ usr/bin /`但是这并没有阻止`bash`使用它自己内置的```实现.首先,它在`man test`和`man bash` /`man builtins`中被注意到.其次,如果你看一下基准测试结果,如果明确使用`/ usr/bin/[`而不是`[`(约13.8分钟与~2.5秒),会有明显的惩罚. (3认同)
  • @ l0b0您应该使用`type`而不是`which`来检查shell如何看到命令以及将执行什么.`type [`:_ [是一个shell builtin_和`type [[`:_ [[是一个shell keyword_ Command```只找到一个命令,对shell builtins一无所知. (3认同)
  • 好评.我感到震惊的是,[[`实际上比``更快.知道为什么吗? (2认同)

Ben*_*son 8

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

与你接近&,你只需要&&[ -f foo ] && [ -d foo ]尽管运行多个命令,而不是一个.

这是test的手册页,它是[指向的链接命令.现代实现test具有更多功能(以及shell内置版本[[,在shell的联机帮助页中有记录).