joH*_*oH1 12 shell filenames test
我需要检查一个目录(我们称之为dir)是否包含两个文件之一(我们称之为filea和fileb),但既不包含一个文件,也不包含两个文件。
理想的解决方案是在谓词之间使用 XOR 运算:
if [ -f dir/filea ] ^ [ -f dir/fileb]
then
echo Structure ok
# do stuff
fi
Run Code Online (Sandbox Code Playgroud)
然而,shell 不支持^作为 XOR 运算符,并且该[命令没有选项-X或--xor像它那样具有-aand -o...使用否定相等也不起作用:
if ! [ -f dir/filea -eq -f dir/fileb ]
# or
if ! [ -f dir/filea = -f dir/fileb ]
Run Code Online (Sandbox Code Playgroud)
有没有什么方法可以实现这一点,而不需要求助于像这样的成熟的 AND/OR 表达式
if { [ -f dir/filea ] || [ -f dir/fileb ]; } && ! { [ -f dir/filea ] && [ -f dir/fileb ]; }
Run Code Online (Sandbox Code Playgroud)
?
最后一个表达式变得不可读,当然我的实际路径比dir/fileX.
编辑:我的目标是 POSIX 兼容版本sh,但我对特定于其他 shell 的扩展持开放态度(主要是出于好奇,但也因为我在其他项目上使用bash或ksh93,这可能在那里有用)
Bod*_*odo 17
test ...或命令的退出代码[ ... ]是测试结果。您可以使用变量来存储各个测试的结果并在以后进行比较。
[ -f dir/filea ]
testA=$?
[ -f dir/fileb ]
testB=$?
if [ "$testA" -ne "$testB" ]
then
echo "exactly one file"
else
echo "both files or none"
fi
Run Code Online (Sandbox Code Playgroud)
这可能会[导致两个测试产生不同的非零退出代码。
根据https://pubs.opengroup.org/onlinepubs/007904875/utilities/test.html中的规范,退出代码>1表示“发生错误”。您必须定义[报告错误时应该发生的情况。
为了避免这种情况,您可以使用类似于Kusalananda 的答案的条件变量赋值......
testA=0
testB=0
[ -f dir/filea ] || testA=1
[ -f dir/fileb ] || testB=1
if [ "$testA" -ne "$testB" ]
then
echo "exactly one file"
else
echo "both files or none"
fi
Run Code Online (Sandbox Code Playgroud)
...或使用否定(如注释中所述)来确保该值是0或1。(请参阅https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_02
中的“2.9.2 管道”-“退出状态” )
! [ -f dir/filea ]
testA=$?
! [ -f dir/fileb ]
testB=$?
if [ "$testA" -ne "$testB" ]
then
echo "exactly one file"
else
echo "both files or none"
fi
Run Code Online (Sandbox Code Playgroud)
两种变体都以与“文件不存在”相同的方式处理错误。
下面对和^的值使用算术异或运算符。如果相应的文件存在并且是普通文件,则这些变量为 1;否则,它们为零。ab
a=0
b=0
[ -f dir/filea ] && a=1
[ -f dir/fileb ] && b=1
[ "$(( a ^ b ))" -eq 1 ] && echo OK
Run Code Online (Sandbox Code Playgroud)
另一种方法是计算测试成功的次数:
ok=0
[ -f dir/filea ] && ok=$(( ok + 1 ))
[ -f dir/fileb ] && ok=$(( ok + 1 ))
[ "$ok" -eq 1 ] && echo OK
Run Code Online (Sandbox Code Playgroud)
值得注意的是,复合命令具有退出状态,就像任何其他命令一样,因此您可以使用与大多数类 C 语言中if a; then b; else c; fi相同的方式。a ? b : c
a^b将\xe2\x89\xa1翻译a ? !b : b成 shell,我们得到:
if if [ -f fileA ]\n then ! [ -f fileB ]\n else [ -f fileB ]\n fi\nthen\n echo ONE file present\nelse\n echo NO or BOTH files present\nfi\nRun Code Online (Sandbox Code Playgroud)\n(请注意,这不是if if拼写错误。)
我承认,必须重复其中一项测试有点冗长,因此我将它们垂直对齐,以明确它们除了反转之外是相同的!。
从好的方面来说,这避免了与临时变量保持$?或类似的混乱,并且它(勉强)是性能最好的。
最后,我要指出的是,该命令-a的 和-o选项test是有问题的:它们可能导致解析歧义,从而导致错误结果。只需使用两个用&&或分隔的测试命令即可||。