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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.