通常在编写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✖
我在这里缺少什么?
答案 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