0

I am writing a script using a function that is it supposed to install a package by giving to the function the name of the package.

I am asking to the user if he wants to install that packet and, to do so, I wrote a regex, but the thing is: my regex is ignored, I can write anything in the output it will works. Howewer, I want the user to write something specific in order to install the package.

#!/bin/bash

OK=$'\033[92m' #GREEN
BASIC=$'\033[96m' #BLUE
RESET=$'\033[0m' #RESET COLOR
CHECK_MARK=$'\033[0;32m\xE2\x9C\x94\033[0m' #CHECKMARK

function install_package() {
    answer=""
    while [[ ! ($answer =~ ^y$|Y$|Yes$|YES$|yes$|n$|N$|no$|No$|NO$|q$|Q$|Quit$|quit$) ]]
    do
        echo -en "${BASIC}Do you want to install $1 ? (y|n|q)${RESET}"
        answer=$(read) 
        if [[ $answer =~ ^y$|Y$|Yes$|YES$|yes$ ]]
        echo "test"
        then 
            echo -ne "${OK}Installation of $1 ${RESET}"
            apt-get install "$1" -y &>/dev/null
            echo -e "\\r${CHECK_MARK}${OK} $1 has been installed or is already installed. ${RESET}"
            break
        fi
    done

}

install_package "micro"

echo "test"

And my output:

root@test-deb:/home/user/bashthings# ./p.sh 
Do you want to install micro ? (y|n|q)y
test
✔ micro has been installed or is already installed. 
test
root@test-deb:/home/user/bashthings# ./p.sh 
Do you want to install micro ? (y|n|q)fege8geg655eg
test
✔ micro has been installed or is already installed. 
test
root@test-deb:/home/user/bashthings# 

It might be confusing but what I am asking is why my regex is not filtering any of what I type ?

3
  • 1
    Please do not write output of apt-get to /dev/null. Write it to a log file instead, e.g. apt-get install "$1" -y 2>&1 >> /var/log/my_script.log and also output a message that tells the user that the log file is available. It will make problem resolution easier for the user. Commented Mar 9, 2022 at 15:30
  • 1
    And also, put set -e to the start of the script. It will terminate the script if an error occurs. (Or handle errors. apt-get can sometimes fail in very strange ways and for weird reasons. Misconfigured dpkg is frequent source of weird failures.) Instead of set -e, you can also add -e to the shebang line: #! /bin/bash -e Commented Mar 9, 2022 at 15:31
  • the echo "test" is between the if and its associated then; I'd have to think about that for a bit but I'm guessing this is treated similarly to if [[ $answer =~ ... ]] || echo "test"; then ..., and since echo "$test" is treated as 'true' the then block is always executed; move the echo "$test" somewhere else (eg, before the if; between the then and fi; after the fi) and see if your script now behaves as desired Commented Mar 9, 2022 at 16:35

2 Answers 2

1

Use grep -q (or egrep -q for extended version).

#            ↓ your string         ↓ the pattern
if printf %s "my_string" | grep -q '.*stri.*'
then
    echo foo
else
    echo bar
fi

Your while loop condition will look like this:

while printf %s $answer | grep -q '^y$|Y$|Yes$|YES$|yes$|n$|N$|no$|No$|NO$|q$|Q$|Quit$|quit$'
do
    : your loop contents here
done

In your conditions, you do not need to use regular expressions at all. Bash has neat syntax construct called case (similar to C switch):

read -p "${BASIC}Do you want to install $1 ? (y|n|q)${RESET}" answer
case "$answer" in
    [yY])
        echo -ne "${OK}Installation of $1 ${RESET}"
        # […]
        echo -e "\\r${CHECK_MARK}${OK} $1 has been installed or is already installed. ${RESET}"
        ;;
    [nN])
        : do nothing
        ;;
    [qQ])
        exit 0
esac

Case uses simple patterns. y means “exactly y”, you can use [yY] to provide multiple character variants. Separate multiple patterns by | for “or”. Asterisk (*) means “zero or more characters”, question mark (?) is one character.

Note also that I used read -p PROMPT VAR to read a text into a variable while displaying a prompt. See read help for more information, it provides quite a lot of features for reading text in such scripts.

Sign up to request clarification or add additional context in comments.

4 Comments

I don't know that much about case I will look into it.
Okay, I see the thing but I want to add some flexibility for my user, like if they type Y or yes instead of y it will still works, and from what I understand here using case will match only one precise string
See the edits. Use [yY].
Ouups my bad didn't see that ! I was trying to do like so but I still have some problems. I will try to solve them alone, I think you put me in the right direction
0

I'd take advantage of the nocasematch shell option that can compare strings case-insensitively, and a case statement can add readability:

shopt -s nocasematch

function install_package() {
    local package=$1
    local answer

    while true; do
        echo -en "${BASIC}Do you want to install $package ? (y|n|q)${RESET}"
        read -r answer
        case $answer in
            n | no | q | quit)
                echo "ok, bye"
                exit
                ;;
            y | yes)
                echo -ne "${OK}Installation of $package ${RESET}"
                #apt-get install "$package" -y &>/dev/null
                echo -e "\\r${CHECK_MARK}${OK} $package has been installed or is already installed. ${RESET}"
                break
                ;;
        esac
    done
}

But the select statement is very useful for these kind of prompts: it prints a menu and the user selects one of the options. It loops until the user enters a valid value.

function install_package() {
    local package=$1
    local answer

    printf "${BASIC}Do you want to install $package ?${RESET}\n"
    PS3="Your answer: "

    select answer in Yes No; do
        case $answer in
            No)
                echo "ok, bye"
                break
                ;;
            Yes)
                echo -ne "${OK}Installation of $package ${RESET}"
                #apt-get install "$package" -y &>/dev/null
                echo -e "\\r${CHECK_MARK}${OK} $package has been installed or is already installed. ${RESET}"
                break
                ;;
        esac
    done
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.