![shell glob 中的异或](https://linux22.com/image/188356/shell%20glob%20%E4%B8%AD%E7%9A%84%E5%BC%82%E6%88%96.png)
最近,我的托管提供商在我不知情的情况public_html
下将我的目录名称更改为,因此此变量声明不再有效。www
web_application_root="${HOME}/public_html"
对于类似的未来情况,不一定在该托管环境中,我想指示 Bash 创建一个基于public_html"
XOR的变量www
。
如果以及如何可能?
答案1
我不认为你能做得更好
web_application_root="${HOME}/public_html"
if [ ! -d "$web_application_root" ] ; then
web_application_root="${HOME}/www"
fi
以各种形式。例如
for loc in "${HOME}/public_html" "${HOME}/www" ; do
web_application_root=$loc
[ -d "$loc" ] && break
done
本质上是找到第一个存在的目录并使用它。
答案2
我将尝试在这里回答主题中的问题。
通配符是通配符模式的扩展,例如扩展*.txt
到与其匹配的文件路径列表中。
files=(~/*.txt)
分配非隐藏文件的列表,其名称以数组的单独元素~
结尾(当 glob 与任何文件都不匹配时,shell 之间的行为会有所不同)。.txt
$files
XOR(A, B)
如果A
或B
都为真但不是两者都为真,则为真,所以就像AND(OR(A, B), NOT(AND(A, B)))
虽然我不知道有哪个 shell 具有XOR
glob/通配符运算符,但有几个 shell 有一个OR
and NOT
,这意味着AND
(where AND(X, Y)
can also be writeNOT(OR(NOT(X), NOT(Y)))
也可以在那里表达,结果XOR
也是如此 (zsh 也有一个AND-NOT
,这使得AND
AND-NOT(X, NOT(Y))
)。
如果我们结合这些公式,我们得到:
XOR(A, B) == NOT(OR(NOT(OR(A, B)), NOT(NOT(NOT(OR(NOT(A), NOT(B)))))))
我们可以将其简化(删除双重否定)为:
XOR(A, B) == NOT(OR(NOT(OR(A, B)), NOT(OR(NOT(A), NOT(B)))))
一旦翻译成ksh
or bash -o extglob
(或zsh -o kshglob
) 全局变量,就变成:
!(!(A|B)|!(!(A)|!(B)))
或者使用zsh -o extendedglob
glob:
^(^(A|B)|^(^A|^B))
或者使用AND-NOT
:
XOR(A, B) == AND-NOT(OR(A, B), NOT(NOT(AND-NOT(A, NOT(B)))))
我们可以简化为:
XOR(A, B) == AND-NOT(OR(A, B), AND-NOT(A, NOT(B)))
曾经翻译成zsh -o extendedglob
glob:
(A|B)~(A~^B)
例如,要查找foo*
与 和匹配*.txt
但不是两者匹配的文件,您可以:
与ksh
或bash -o extglob
(或zsh -o kshglob
):
files=(!(!(foo*|*.txt)|!(!(foo*)|!(*.txt))))
或者与zsh -o extendedglob
:
files=(^(^(foo*|*.txt)|^(^foo*|^*.txt)))
或者:
files=((foo*|*.txt)~(foo*~^*.txt))
POSIX shell 有一个二进制(按位)XOR 算术运算符:$(( 1 ^ 2 ))
扩展为,3
因为它是XOR(0b01, 0b10)
.
因此,您可以定义一个xor
返回 true 的函数,如果两个 shell 代码的评估中的一个但不是同时返回 true,则返回 true ,如下所示:
xor() {
eval "$1"; s1=$((!$?))
eval "$2"; s2=$((!$?))
[ "$((s1 ^ s2))" -ne 0 ]
}
match() {
case $1 in
($2) true;;
(*) false;;
esac
}
for file in *; do
if
xor 'match "$file" "foo*"' \
'match "$file" "*.txt"' &&
then
printf '%s\n' "$file"
fi
done