Looping input command - shell

How can we have input from user in DD/MM/YYYY format in shell code such that till user does not provides input in this format the same prompt is printed again and again.
printf "Please Enter START Date(DD/MM/YYYY)\n"
read date1
how to check in this that if date is provided in DD/MM/YYYY format or not. and if not then how to start this loop again?

This solution firstly checks the syntax of all the tokens
of the input and then using the command date checks if the
the input is a valid date. For instance 30/02/2014 has
a correct syntax but it is not a valid date. Please note
that the date command accepts the format MM/DD/YYYY.
#!/bin/bash
while [ 1 ]; do
IFS="/" read -p "Please Enter START Date(DD/MM/YYYY): " d m y
if [[ $d != [0-9][0-9] ]]; then echo "Day format invalid"; continue; fi
if [[ $m != [0-9][0-9] ]]; then echo "Month format invalid"; continue; fi
if [[ $y != [0-9][0-9][0-9][0-9] ]]; then echo "Year format invalid"; continue; fi
if date -d "$m/$d/$y" > /dev/null; then
date1="$d/$m/$y"
break
fi
done
# Here you can use $date1 as you prefer. For instance:
echo $date1

Try this one:
until read -p "Please Enter START Date (DD/MM/YYYY): " && [[ $REPLY == [0-9][0-9]/[0-9][0-9]/[0-9][0-9] ]]; do
## echo "Please enter in proper (DD/MM/YYYY) format."
continue
done
The echo part is just optional that you could consider adding.
By default, read places input in $REPLY variable if no variable is specified, but you could have a custom one if you like.
read -p "Please Enter START Date (DD/MM/YYYY): " INPUT && [[ $INPUT == ...
For a bit more sh-compatible and based on your own format you can have this:
#!/bin/sh
while :; do
printf "Please Enter START Date(DD/MM/YYYY)\n"
read date1
case "$date1" in
[0-9][0-9]/[0-9][0-9]/[0-9][0-9])
break
;;
esac
done

Related

Value from pattern - bash

I need to get value from <2018-2099>, if user will type wrong value then script will tell him that value is incorrect and will ask him to type again.
I already have something like this but it doesn't work..
Any suggestions?
#!/bin/bash
read -r -p "Type year [value from 2019-2099]" year
if [[ "$year" =~ ^(20[1-9]|[1-9])+$ ]]; then
mkdir -p "/home/$year/"
else
echo "$year - value is not correct. Try again." >&2 && exit 1
fi
You can use function and until loop to achieve this, consider following code:
readYear() {
read -r -p "Type year [value from 2018-2099]" year
[[ "${year}" =~ ^[0-9]{4}$ ]] && [[ "${year}" -ge 2018 ]] && [[ "${year}" -le 2099 ]]
}
until readYear; do
echo "${year} - value is not correct. Try again." >&2
done
The function returns 0 if the value entered is valid, then the loop terminates.
You can try this:
until
read -r -p "Type year [2019-2099]: " year
[[ $year =~ ^2[0-9]+$ ]] && (( year >= 2018 && year <= 2099 ))
do
echo "Incorrect value" >&2
done
echo "OK: $year"

How to check if the user has entered a single letter?

I am reading a character and want to check if that is single character and is a letter. My code is below:
#!/usr/bin/bash
read -p "Enter something: " char
if [[ ${#char} != 1 && "$char" != *[a-z]* ]]; then
echo "Not a valid input"
else
echo "Its a valid input"
fi
The o/p is below:
[root#host-7 ~]# sh -x t
+ read -p 'Enter something: ' char
Enter something: 1
+ [[ 1 != 1 ]]
+ echo 'Its a valid input'
Its a valid input
While executing the script only first condition is getting executed and its not checking the second condition.
It is not evaluating 2nd condition because first condition is failing as you're entering only 1 character in input and there is && between 2 conditions.
If you enter 2 character input like ab then you'll see both conditions getting evaluated.
You can use -n1 to restrict input to one character only like this:
#!/usr/bin/bash
read -n1 -p "Enter something: " char
echo
if [[ "$char" != *[a-z]* ]];then
echo "Not a valid input"
else
echo "Its a valid input"
fi
And run it as:
bash -x ./t
If you wish to test for a single letter, you can use the POSIX character class [[:alpha:]] or equivalently [a-zA-Z]. No need to check the length or use wildcards as a single bash pattern will only test multiple occurrences of a match when using extglob optional patterns.
read -p "Enter something: " char
if [[ $char != [[:alpha:]] ]]; then
echo "Not a valid input"
else
echo "It's a valid input"
fi
You can simply change the if statement to:
if [[ $char != [a-zA-Z] ]]; then
It will check if $char is a single character and is a letter (a-z,A-Z).
Example:
#!/bin/bash
read -p "Enter something: " char
if [[ $char != [a-zA-Z] ]]; then
echo "Not a valid input"
else
echo "Its a valid input"
fi
Output will be like this:
ab : inavlid
a : valid
1 : invalid
12 : invalid
A : valid
AB : invalid

sign automatic number to my file in bash

I am trying to assign a unique number to my strings that are redirected and stored in a file.
You have to fill in a form, and I want to send a unique number with it.
example:
echo fill in the form
echo place
read place
date
"place: $place, time $(date) >> List
It has to look something like this.
outcome in List
number 1, place, time
number 2, place, time
number 3, place, time
I used a loop but I got the following outcome.
number 0, place, time
number 0, place, time.
I think I need a function that checks the last number given in the file and add 1 to it, but I wonder if there is an easier way.
Perhaps this one:
#!/bin/bash
# Optionally truncate file
# : > List
I=0
while
read -p "Place: " PLACE
read -p "Time: " TIME
echo "number $((++I)), $PLACE, $TIME" >> List
read -n 1 -p "Continue? " && [[ $REPLY == [yY] ]]
do
continue
done
Update:
#!/bin/bash
# Optionally truncate file
# : > List
shopt -s extglob
for (( I = 1;; ++I )); do
for (( ;; )); do
read -p "Place: " PLACE
read -p "Time: " TIME
until
read -p "Save data? "
[[ $REPLY == [nN]?([oO]) ]]
do
[[ $REPLY == [yY]?([eE][sS]) ]] && break 2
echo "Please answer Y[es] or N[o]."
done
done
echo "Saving \"number $I, $PLACE, $TIME\"."
echo "number $I, $PLACE, $TIME" >> List
until
read -p "Continue? "
[[ $REPLY == [yY]?([eE][sS]) ]]
do
[[ $REPLY == [nN]?([oO]) ]] && break 2
echo "Please answer Y[es] or N[o]."
done
echo
done
To generate a 16-char random hex string, you can use r=$(openssl rand -hex 8)
To find the last number used and increment it, you can do
prev=$(awk -F, 'END {print $1}' List)
printf "%d, place:%s, time:%s\n" $((prev+1)) "$place" "$(date)" >> List
This is subject to a race condition if the script can be executed simultaneously
To start at 1:
if [[ ! -f List ]]; then
prev=1
else
prev=$(awk -F, 'END {print $1}' List)
fi
printf "%d, place:%s, time:%s\n" $((prev+1)) "$place" "$(date)" >> List

Multiple test [[ expressions ]] in Bash

I asked a question yesterday about trying to make sure that a date is formatted correctly. This is slightly more targeted as I've tried to write the test myself and it's not acting as I would have expected it to. (I'm new to linux, so this happens a fair amount)
I need the date inputted as YYYYMMDD. I think that the start of the if statement should be checking basically [[ check if $tDate is a zero string or if the date is not YYYYMMDD ]].
When I run the code the date is there $lastCOB (yesterday, last close of business), but when I input any date (or don't change the date), I get the "Invalid date. Please enter the correct format." response, meaning that obviously the test doesn't believe the format to be correct, though it's entered as YYYYMMDD as i want it to be.
tDate=$(lastCOB)
tDateOkay=0
until [[ $tDateOkay -eq 1 ]] ; do
echo "$tDate"
echo "$tDateOkay"
read -p "Please Enter date for search. Use format: Date (YYYYMMDD): " -e -i "$tDate" tDate
if [[ -z "$tDate" || "$(date --date=$tDate + '%Y%m%d' 2>&1 )" != $tDate ]] ; then
echo "Invalid date. Please enter date in the correct format."
elif [[ $tDate -gt $(today) || $tDate -eq $(today) ]] ; then
echo "Date must be in the past. Please try again."
else
tDateOkay=1
fi
done
The first if statement should test for format. the elif statement should test for making sure that the date is in the past, and not future, or even today. If it passes both tests, then tDateOkay should turn to 1 from zero and the program should move on with that date as the input for the future data search. Let me know if I need to add anything for clarity.
I think you have a problem in this line:
if [[ -z "$tDate" || "$(date --date=$tDate + '%Y%m%d' 2>&1 )" != $tDate ]] ; then
It works to me with
if [[ -z "$tDate" || "$(date --date=$tDate '+%Y%m%d' 2>&1 )" != $tDate ]] ; then
That is, put the + inside the date format.
For the future, I recommend you to use 2>&1 when you are sure the code is fine. Otherwise it is more complicated to debug.
By the way, as stated by an answer that was deleted, the if [[ || ]] is not the best POSIX way. In this comment I was recommended to use if [ ] || [ ].

Test the date format to make sure it has been input correctly

I'm trying to write a test in bash that will check that a date has been entered correctly (or that a date has been entered at all). Here is what I'm trying to do:
tDate=$(lastCOB)
tDateOkay=0
until [ $tDateOkay -eq 1 ] ; do
read -p "Please Enter date for search. Use format: Date (YYYYMMDD): " -e -i "$tdate" tDate
if [[ -z "$tDate" || {check for valid YYMMDD format}]] ; then
echo "Invalid date. Please enter date in the correct format."
elif [[ $tDate -gt $(today)|| $tdate -eq $(today) ]] ; then
echo "Date must be in the past. Please try again."
else
tDateOkay=1
fi
done
The date has to be in the past and has to be written in the correct format, or the data won't be pulled from the correct folder. Thanks.
# other stuff
elif (( `date +%s -d $tDate` >= `date +%s` ))
then
echo 'Date must be in the past. Please try again.'
# other stuff

Resources