Loop a user input until it passes validation - regex

I have the following in a bash script:-
#!/bin/bash
re="/(\W|^)php[5-9]{1}.[0-9]{1}-fpm.sock(\W|$)/gm"
while ! [[ "${socket}" =~ ${re} ]]
do
echo "enter socket string:"
read socket
done
A valid $socket string from the user would equal php8.1-fpm.sock using the regex we're testing for. What actually happens is, the loop continues with a user unable to break out of it despite a valid string?
I should be able to use my $socket variable in the script following a successful validation. What am I missing?
Edit:
Stuff I've tried:-
re="/php[5-9]{1}.[0-9]{1}-fpm.sock/" omitting (\W)

This could be done more correctly for the match, using standard globbing pattern matching of case in, with POSIX-shell grammar only.
#!/bin/sh
while case $socket in *php[5-9].[0-9]-fpm.sock) false ;; esac; do
printf 'Enter socket string: '
read -r socket
done
It could even test the socket is really an actual socket by testing -S:
#!/bin/sh
while case $socket in *php[5-9].[0-9]-fpm.sock) ! [ -S "$socket" ] ;; esac; do
printf 'Enter socket string: '
read -r socket
done
See man test:
-S FILE
FILE exists and is a socket

Related

A script to insert email address into sender_access_regexp (and add a regexp)

I, a complete noob even by skiddie standards, am trying to write a script that would do following:
ask for an email address (as an input)
run sudoedit on /etc/postfix/sender_access_regexp (as current user)
check if duplicate lines exist (meaning if such an address is already listed in above mentioned file)
insert the provided email address if it is unique
add /^.* before address
append $/ REJECT after address
write and quit
run postmap /etc/postfix/sender_access_regexp (as current user)
run postfix reload (as current user)
So far, I've got the easy part done, getting the script to ask for an email address, just so:
echo -e "\nPlease enter user's email address to add to Sender Access blocklist:"
read -p 'Email:' USERMAIL
I'm not lazy and looking only for a copy/paste solution (but I won't mind one), so if you can point me in the right direction, that would be much appreciated as well.
Thanks in advance.
Well, digging a wee bit deeper, I've managed to piece together different bits and pieces to form a functioning script. It is by far not the prettiest beast out there, but considering it was literally my first - I'm happy :) I'll share it here in it's entirety in case someone else needs it as well.
#!/bin/bash
## USERMAIL is a mail address to be added to the blocklist
COUNTER=0
while [ $COUNTER -lt 1 ]; do
clear ## clears the screen
echo -e "\nPlease enter user's email address to add to Sender Access blocklist (to minimize errors, please copy the email address):"
echo ""
read -p 'Email:' USERMAIL ## this is where user provides an email
if
grep $USERMAIL /etc/postfix/sender_access_regexp;
then echo -e "\nUser already exists on this list." ## this checks if there is such an address on the list already
else
echo /^.*$USERMAIL$/ REJECT >> /etc/postfix/sender_access_regexp ## the bits and pieces surrounding $USERMAIL are all part of regex
echo -e "\nUser $USERMAIL added to blocklist."
echo ""
fi
echo""
read -p "Would you like to add another addres to Sender Access blocklist? (y/n)" -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]
then let COUNTER=COUNTER+0
echo ""
else let COUNTER=COUNTER+1
echo ""
fi
done
postmap /etc/postfix/sender_access_regexp
postfix reload
echo ""
read -n 1 -s -r -p "All done, press any key to continue"
echo ""
There, that's it. All those echo "" are there because I didn't know how to add an empty line :shrug:
P.S. you might get permission denied if the owner of the /etc/postifx/sender_access_regexp and your script are not the same user.
P.P.S. all suggestions on smoothing this script out are still welcome!

FreeBSD Bash - Unable to code a condition with regex

I am trying to code a script for pfSense who is based on FreeBSD. The only part left who is giving me trouble is a condition with a regex. The simplified code is this :
RESPONSE='{"port":98989}'
REG='{"port":([0-9]*)}'
if [[ $RESPONSE =~ $REG ]]; then
PORT=${BASH_REMATCH[1]}
fi
With the trace mode enabled, the error returned is the following :
+ RESPONSE='{"port":98989}'
+ REG='{"port":([0-9]*)}'
+ '[[' '{"port":98989}' '=~' '{"port":([0-9]*)}' ]]
./pia-port-v2: [[: not found
I don't understand why the [[ is between single quote in the trace and it is probably why the "not found" error occurs.
Update
It is probably because pfSense's FreeBSD does not support bash and these instructions are bash only. I found that after writing this question and trying to find an answer.
Anybody have an alternative for bourne shell? The goal is to return the port number if the expression match.
I am new to script coding in unix like OS.
In the meantime, I look at grep, but it seems to apply the regex to file input only.
You should be able to use the expr utility to do this, but note that it use Posix basic regexps, which means that you need to backslash your parentheses to make them into captures:
response='{"port":98989}'
reg='{"port":\([0-9]*\)}'
port=$(expr "$response" : "$reg")
expr returns failure if the regex doesn't match, so you could use a shell conditional to test:
port=$(expr "$response" : "$reg") || { echo Failed; }
or
if ! port=$(expr "$response" : "$reg"); then
# Do something on failure
fi
With /bin/sh:
#!/bin/sh
response='{"port":98989}'
case $response in
'{"port":'[0-9]*'}')
port=${response#*:} # {"port":98989} --> 98989}
port=${port%'}'} # 98989} --> 98989
esac
printf 'response %s yields port %s\n' "$response" "$port"
Note that a case statement does not use regular expression but shell filename globbing patterns. Therefore, the pattern will only match a single digit and trigger for bogus strings like {"port":0xxx}.
If the response string is a JSON document:
$ response='{"port":98989}'
$ printf '%s\n' "$response" | jq .port
98989
There is trouble with ' and " when using [[ regexps (sometimes; not always) so I would try this instead (which works fine for me):
#!/bin/bash
REG=\{\"port\"\:\([0-9]\*\)\} # This line is altered
RESPONSE='{"port":98989}'
if [[ $RESPONSE =~ $REG ]]; then
echo funkar
fi

Regular expressions don't work as expected in bash if-else block's condition

My pattern defined to match in if-else block is :
pat="17[0-1][0-9][0-9][0-9].AUG"
nln=""
In my script, I'm taking user input which needs to be matched against the pattern, which if doesn't match, appropriate error messages are to be shown. Pretty simple, but giving me a hard time though. My code block from the script is this:
echo "How many days' AUDIT Logs need to be searched?"
read days
echo "Enter file name(s)[For multiple files, one file per line]: "
for(( c = 0 ; c < $days ; c++))
do
read elements
if [[ $elements =~ $pat ]];
then
array[$c]="$elements"
elif [[ $elements =~ $nln ]];
then
echo "No file entered.Run script again. Exiting"
exit;
else
echo "Invalid filename entered: $elements.Run script again. Exiting"
exit;
fi
done
The format I want from the user for filenames to be entered is this:
170402.AUG
So basically yymmdd.AUG (where y-year,m-month,d-day), with trailing or leading spaces is fine. Anything other than that should throw "Invalid filename entered: $elements.Run script again. Exiting" message. Also I want to check if if it is a blank line with a "Enter" hit, it should give an error saying "No file entered.Run script again. Exiting"
However my code, even if I enter something like "xxx" as filename, which should be throwing "Invalid filename entered: $elements.Run script again. Exiting", is actually checking true against a blank line, and throwing "No file entered.Run script again. Exiting"
Need some help with handling the regular expressions' check with user input, as otherwise rest of my script works just fine.
I think as discussed in the comments you are confusing with the glob match and a regEx match, what you have defined as pat is a glob match which needs to be equated with the == operator as,
pat="17[0-1][0-9][0-9][0-9].AUG"
string="170402.AUG"
[[ $string == $pat ]] && printf "Match success\n"
The equivalent ~ match would be to something as
pat="17[[:digit:]]{4}\.AUG"
[[ $string =~ $pat ]] && printf "Match success\n"
As you can see the . in the regex syntax has been escaped to deprive of its special meaning ( to match any character) but just to use as a literal dot. The POSIX character class [[:digit:]] with a character count {4} allows you to match 4 digits followed by .AUG
And for the string empty check do as suggested by the comments from Cyrus, or by Benjamin.W
[[ $elements == "" ]]
(or)
[[ -z $elements ]]
I would not bug the user with how many days (who want count 15 days or like)? Also, why only one file per line? You should help the users, not bug them like microsoft...
For the start:
show_help() { cat <<'EOF'
bla bla....
EOF
}
show_files() { echo "${#files[#]} valid files entered: ${files[#]}"; }
while read -r -p 'files? (h-help)> ' line
do
case "$line" in
q) echo "quitting..." ; exit 0 ;;
h) show_help ; continue;;
'') (( ${#files} )) && show_files; continue ;;
l) show_files ; continue ;;
p) (( ${#files} )) && break || { echo "No files enterd.. quitting" ; exit 1; } ;; # go to processing
esac
# select (grep) the valid patterns from the entered line
# and append them into the array
# using the -P (if your grep know it) you can construct very complex regexes
files+=( $(grep -oP '17\d{4}.\w{3}' <<< "$line") )
done
echo "processing files ${files[#]}"
Using such logic you can build really powerful and user-friendly app. Also, you can use -e for the read enable the readline functions (cursor keys and like)...
But :) Consider just create a simple script, which accepts arguments. Without any dialogs and such. example:
myscript -h
same as above, or some longer help text
myscript 170402.AUG 170403.AUG 170404.AUG 170405.AUG
will do whatever it should do with the files. Main benefit, you could use globbing in the filenames, like
myscript 1704*
and so on...
And if you really want the dialog, it could show it when someone runs the script without any argument, e.g.:
myscript
will run in interactive mode...

bash Regular Expression in if Statement

I am stumped at the error I am getting from a script I'm trying to run to suspend my machine. I am trying to use a regex in an elif statement to suspend my machine after a specific time period.
#!/bin/bash
echo "When would you like to suspend the machine?"
read "sustime"
if [ "$sustime" = "now" ]
then
sudo pm-suspend
elif [[ "$sustime" =~ [0-9]*[smhd] ]]
then
time=`expr "$sustime" : '\([0-9]+)\)'`
ttype=`expr "$sustime" : '.*\([smhd]\)'`
sudo sleep $time$ttype ; sudo pm-suspend
else
echo "Please enter either [now] or [#s|m|h|d]"
fi
The code doesn't work on the elif line, for example if I input 5s, the output of the script is:
$ sh dbussuspend.sh
When would you like to suspend the machine?
5s
dbussuspend.sh: 10: dbussuspend.sh: [[: not found
Please enter either [now] or [#s|m|h|d]
However, it should read that I've entered the string 5s run the code block under elif. I've actually tried any regex in place of [0-9]*[smhd], all with the same error.
this problem is not caused by your script, but by how you're invoking it:
sh dbussuspend.sh
should be:
bash dbussuspend.sh
bash know how to [[, but sh doesn't...
better still, do as Gordon Davisson suggests. do this once:
chmod +x dbussuspend.sh
and thenceforth, invoke like this:
./dbussuspend.sh
additionally, Etan Reisner and chepner question your use of expr, and laurel your bash regex. GNU coreutils sleep supports e.g. sleep 30s, sleep 2m, sleep 1h. check this on your system with man sleep. if so, then this will work:
elif [[ "$sustime" =~ ^[0-9]+[smhd]$ ]]
then
sudo sleep $sustime ; sudo pm-suspend
(^ and $ in ^[0-9]+[smhd]$ to match the start and end of the string and prevent matching e.g. "uzeifue1sziufzr".)

shell script - store output of "kill -HUP 1234" (gunicorn command) to variable and check if the variable contains "No such process"

I tried below command to store output to variable :
k=$(kill -HUP 1234) #command getting executed but not storing to variable
echo "$k"
For checking if the output contains the word, i can do like this :
if [[ outputvar =~ .*No such process*. ]]
As per the examples, above should work , but i donno why its not working . Can you tell me correct way so that if I cannot restart the process, I can atleast start a new process with direct gunicorn_django command
Usually such error messages are output on the stderr stream, while the $() construct returns only the stdout stream. You need to redirect stderr to stdout:
k=$(kill -HUP 1234 2>&1)
Your regular expression has an error: the *. at the end must be .*; you will also need to quote your strings in your test:
if [[ "$k" =~ .*No\ such\ process.* ]]
Note that "$k" is quoted, while the spaces in the regular expression are escaped; you cannot quote the regular expression (as ".*No such process.*"), as using a quoted string in a =~ test forces string match instead of a regular expression match.