0

Recently my hosting provider changed my public_html directory's name to www without me knowing it so this variable declaration web_application_root="${HOME}/public_html" no longer works.

For similar future cases, not necessarily in that hosting environment, I want to instruct Bash to create a variable based on public_html" XOR www.

If and how would that be possible?

  • I am not sure how the XOR operator applies to strings. Can you elaborate? Perhaps you mean "web_application_root gets assigned the value public_html or www, depending on the name of the directory in $HOME". If so, a simple if [ -d $HOME/www]; then ...; else ...; fi does the trick. – berndbausch Feb 19 '21 at 09:26
  • @berndbausch In case the string represents a directory name; only one of the two directory names (i.e the one which extists) should be picked. – timesharer Feb 19 '21 at 09:27
  • That's what I guessed. I edited my comment. – berndbausch Feb 19 '21 at 09:31

2 Answers2

1

I don't think you can do better than

web_application_root="${HOME}/public_html"
if [ ! -d "$web_application_root" ] ; then
   web_application_root="${HOME}/www"
fi

in various guises. e.g.

for loc in "${HOME}/public_html" "${HOME}/www" ; do
   web_application_root=$loc
   [ -d "$loc" ] && break
done

Essentially find the first directory which exists and use that.

icarus
  • 17,920
1

I'll try to answer the question in the subject here.

Globbing is the expansion of a wildcard pattern such as *.txt into the list of file paths matching them.

files=(~/*.txt)

assigns the list of non-hidden files in ~ whose name ends in .txt as separate elements of the $files array (with variation in behaviour between shells when the glob doesn't match any file).

XOR(A, B) is true if A or B are true but not both, so is like AND(OR(A, B), NOT(AND(A, B)))

While I don't know of any shell that has a XOR glob/wildcard operator, several shells have a OR and NOT, which means AND (where AND(X, Y) can also be written NOT(OR(NOT(X), NOT(Y))) can also be expressed there and as a result XOR as well (zsh also has a AND-NOT, which makes AND AND-NOT(X, NOT(Y))).

If we combine those formulas, we get:

XOR(A, B) == NOT(OR(NOT(OR(A, B)), NOT(NOT(NOT(OR(NOT(A), NOT(B)))))))

Which we can simplify (removing the double-negation) to:

XOR(A, B) == NOT(OR(NOT(OR(A, B)), NOT(OR(NOT(A), NOT(B)))))

Once translated to ksh or bash -o extglob (or zsh -o kshglob) globs, that becomes:

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

Or with zsh -o extendedglob globs:

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

Or using AND-NOT:

XOR(A, B) == AND-NOT(OR(A, B), NOT(NOT(AND-NOT(A, NOT(B)))))

Which we can simplify to:

XOR(A, B) == AND-NOT(OR(A, B), AND-NOT(A, NOT(B)))

Which once translated to zsh -o extendedglob globs:

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

So for instance, to find files that match foo* and *.txt, but not both, you could:

with ksh or bash -o extglob (or zsh -o kshglob):

files=(!(!(foo*|*.txt)|!(!(foo*)|!(*.txt))))

Or with zsh -o extendedglob:

files=(^(^(foo*|*.txt)|^(^foo*|^*.txt)))

Or:

files=((foo*|*.txt)~(foo*~^*.txt))

POSIX shells have a binary (bitwise) XOR arithmetic operator: $(( 1 ^ 2 )) expands to 3 because that's XOR(0b01, 0b10).

So you could define a xor function that returns true if either but not both the evaluation of two pieces of shell code return true with something like:

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