shell glob 中的异或

shell glob 中的异或

最近,我的托管提供商在我不知情的情况public_html下将我的目录名称更改为,因此此变量声明不再有效。wwwweb_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)如果AB都为真但不是两者都为真,则为真,所以就像AND(OR(A, B), NOT(AND(A, B)))

虽然我不知道有哪个 shell 具有XORglob/通配符运算符,但有几个 shell 有一个ORand 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)))))

一旦翻译成kshor bash -o extglob(或zsh -o kshglob) 全局变量,就变成:

!(!(A|B)|!(!(A)|!(B)))

或者使用zsh -o extendedglobglob:

^(^(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 extendedglobglob:

(A|B)~(A~^B)

例如,要查找foo*与 和匹配*.txt但不是两者匹配的文件,您可以:

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

相关内容