1

Here is my code

vmname="$1"
EXCEPTLIST="desktop-01|desktop-02|desktop-03|desktop-04"
if [[ $vmname != @(${EXCEPTLIST}) ]]; then
    echo "${vmname}"
else
echo "Its in the exceptlist"
fi

The above code works perfectly but my question is , the EXCEPTLIST can be a long line, say 100 server names. In that case its hard to put all that names in one line. In that situation is there any way to make the variable EXCEPTLIST to be a multiline variable ? something like as follows:

    EXCEPTLIST="desktop-01|desktop-02|desktop-03| \n
                desktop-04|desktop-05|desktop-06| \n
                desktop-07|desktop-08"

I am not sure but was thinking of possibilities.

Apparently I would like to know the terminology of using @(${})- Is this called variable expansion or what ? Does anyone know the documentation/explain to me about how this works in bash. ?

SAGAR BHOOSHAN
  • 309
  • 2
  • 11
  • 1
    See "Pattern Matching" section of bash manual: `@(pattern-list)` Matches one of the given patterns – jhnc Jan 31 '23 at 07:07
  • A backslash, immediately followed by a carriage return, marks a continuation line. However, for checking whether one string is part of the list, I would rather use an associative array (where its keys are the listelement) instead of a single string. – user1934428 Jan 31 '23 at 07:25
  • 1
    Possible duplicate of [Check if a variable exists in a list in Bash](https://stackoverflow.com/q/8063228/7939871) – Léa Gris Jan 31 '23 at 10:30

6 Answers6

1

One can declare an array if the data/string is long/large. Use IFS and printf for the format string, something like:

#!/usr/bin/env bash

exceptlist=(
 desktop-01
 desktop-02
 desktop-03
 desktop-04
 desktop-05
 desktop-06
)

pattern=$(IFS='|'; printf '@(%s)' "${exceptlist[*]}")

[[ "$vmname" != $pattern ]] && echo good

In that situation is there any way to make the variable EXCEPTLIST to be a multiline variable ?

With your given input/data an array is also a best option, something like:

exceptlist=(
  'desktop-01|desktop-02|desktop-03'
  'desktop-04|desktop-05|desktop-06'
  'desktop-07|desktop-08'
)

Check what is the value of $pattern variable one way is:

declare -p pattern

Output:

declare -- pattern="@(desktop-01|desktop-02|desktop-03|desktop-04|desktop-05|desktop-06)"

  • Need to test/check if $vmname is an empty string too, since it will always be true.

  • On a side note, don't use all upper case variables for purely internal purposes.

  • The $(...) is called Command Substitution.

    • See LESS=+'/\ *Command Substitution' man bash

In addition to what was mentioned in the comments about pattern matching

  • See LESS=+/'(pattern-list)' man bash

  • See LESS=+/' *\[\[ expression' man bash

Jetchisel
  • 7,493
  • 2
  • 19
  • 18
1

s there any way to make the variable EXCEPTLIST to be a multiline variable ?

I see no reason to use matching. Use a bash array and just compare.

exceptlist=(
   desktop-01
   desktop-02
   desktop-03
   desktop-04
   desktop-05
   desktop-06
)

is_in_list() {
   local i
   for i in "${@:2}"; do
      if [[ "$1" = "$i" ]]; then
          return 0
      fi
   done
   return 1
}

if is_in_list "$vmname" "${EXCEPTLIST[@]}"; then
    echo "is in exception list ${vmname}"
fi

@(${})- Is this called variable expansion or what ? Does anyone know the documentation/explain to me about how this works in bash. ?

${var} is a variable expansion.

@(...) are just characters @ ( ).

From man bash in Compund commands:

   [[ expression ]]

          When the == and != operators are used, the string to the right of the operator is considered a pattern and matched according to the rules
          described below under Pattern Matching, as if the extglob shell option were enabled. ...

From Pattern Matching in man bash:

          @(pattern-list)
                 Matches one of the given patterns

[[ command receives the @(a|b|c) string and then matches the arguments.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
1

There is absolutely no need to use Bash specific regex or arrays and loop for a match, if using grep for raw string on word boundary. The exception list can be multi-line, it will work as well:

#!/usr/bin/sh

exceptlist='
desktop-01|desktop-02|desktop-03|
deskop-04|desktop-05|desktop-06|
desktop-07|deskop-08'

if printf %s "$exceptlist" | grep -qwF "$1"; then
  printf '%s is in the exceptlist\n' "$1" 
fi
Léa Gris
  • 17,497
  • 4
  • 32
  • 41
  • 1
    Well done for making it work with `grep`. Normally `grep` would take regex which could lead to unintended regex handling which you disabled with the use of `-F`. The other caveat is sanitizing of inputs to avoid unintended sequences, which you've avoided using quotes. However, there are issues with your solution with "desktop" and "01" appearing as false positive matches. – Stephen Quan Jan 31 '23 at 21:11
0

I wouldn't bother with multiple lines of text. This is would be just fine:

EXCEPTLIST='desktop-01|desktop-02|desktop-03|'
EXCEPTLIST+='desktop-04|desktop-05|desktop-06|'
EXCEPTLIST+='desktop-07|desktop-08'

The @(...) construct is called extended globbing pattern and what it does is an extension of what you probably already know -- wildcards:

VAR='foobar'
if [[ "$VAR" == fo?b* ]]; then
    echo "Yes!"
else
    echo "No!"
fi

A quick walkthrough on extended globbing examples: https://www.linuxjournal.com/content/bash-extended-globbing

Newerth
  • 449
  • 2
  • 12
0

Here's one way to load a multiline string into a variable:

fn() {
  cat <<EOF
desktop-01|desktop-02|desktop-03|
desktop-04|desktop-05|desktop-06|
desktop-07|desktop-08
EOF
}

exceptlist="$(fn)"
echo $exceptlist

As to solving your specific problem, I can think of a variety of approaches.

Solution 1, since all the desktop has the same desktop-0 prefix and only differ in the last letter, we can make use of {,} or {..} expansion as follows:

vmname="$1"
found=0
for d in desktop-{01..08}
do
  if [[ "$vmname" == $d ]]; then
    echo "It's in the exceptlist"
    found=1
    break
  fi
done
if (( !found )); then
  echo "Not found"
fi

Solution 2, sometimes, it is good to provide a list in a maintainable clear text list. We can use a while loop and iterate through the list

vmname="$1"
found=0
while IFS= read -r d
do
  if [[ "$vmname" == $d ]]; then
    echo "It's in the exceptlist"
    found=1
    break
  fi
done <<EOF
desktop-01
desktop-02
desktop-03
desktop-04
desktop-05
desktop-06
desktop-07
desktop-08
EOF
if (( !found )); then
  echo "Not found"
fi

Solution 3, we can desktop the servers using regular expressions:

vmname="$1"
if [[ "$vmname" =~ ^desktop-0[1-8]$ ]]; then
  echo "It's in the exceptlist"
else
  echo "Not found"
fi

Solution 4, we populate an array, then iterate through an array:

vmname="$1"
exceptlist=()
exceptlist+=(desktop-01 desktop-02 desktop-03 deskop-04)
exceptlist+=(desktop-05 desktop-06 desktop-07 deskop-08)
found=0
for d in ${exceptlist[@]}
do
  if [[ "$vmname" == "$d" ]]; then
    echo "It's in the exceptlist"
    found=1
    break;
  fi
done
if (( !found )); then
  echo "Not found"
fi
Stephen Quan
  • 21,481
  • 4
  • 88
  • 75
  • 1
    You don't need the `found` "boolean" variable: `for d in "${exceptlist[@]}"; do [[ "$vmname" == "$d" ]] && break; done && printf '%s is in the exceptlist\n' "$vmname"` – Léa Gris Jan 31 '23 at 09:59
  • Thanks @LéaGris, when I was writing it I was mentally thinking about both the `found` and `!found` cases mentally. The idea being was to show it being calculated but leave it up to the OP to adapt, but, since your feedback, I decided to explicitly show messages for both cases. – Stephen Quan Jan 31 '23 at 21:03
  • The method without boolean variable, works as well to deal with both cases found and not found `if for d in "${exceptlist[@]}"; do [[ "$vmname" == "$d" ]] && break; done then printf '%s is in the exceptlist\n' "$vmname"; else printf '%s not found\n' "$vmname"; fi` – Léa Gris Jan 31 '23 at 21:57
0
#!/bin/bash

set +o posix
shopt -s extglob

vmname=$1

EXCEPTLIST=(
    desktop-01 desktop-02 desktop-03
    ...
)

if IFS='|' eval '[[ ${vmname} == @(${EXCEPTLIST[*]}) ]]'; then
    ...
konsolebox
  • 72,135
  • 12
  • 99
  • 105