Multiple test [[ expressions ]] in Bash - 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 [ ] || [ ].

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"

Checking and appropriately correcting the arguments as the input for the script from command line

I would like to ask you and request for the help with this particular part of the script - I would like to make it more compact as I feel it is too long, or there might me a shorter syntax alternatives...
This part is cut from my script for creation of X amount of files with descending date (1 day jumps, so each file is 1 day older that previous), which is already 3x longer than the whole "implementation" part.
This part's purpose is to maintain, check and correct (appropriately) the arguments defined with the script execution, which looks like
./script.sh X X
whereas script expects two arguments (number indicating amount of files to create and the path to the folder, where it is supposed to create those0:
- if there is just 1 it assumes the user wants to create files in the present folder, therefore it checks, if the one included argument is a number and if it is it will add output from pwd to the path (in this case to not have some sort of "cross-mount" accident with the system variable PATH conveniently named PATHX), so there will be two arguments at the end anyway
- if there are more than 2, it will terminate the script
If there are 2 arguments, it performs additional "tasks:
1. checks if there are just two numbers, if so, script ends
2. checks if there are just two words/letters, if so, script ends
3. if there are, let's say, just two dots as arguments, it will end anyway as there is one if, which always will need one argument to be just number
4. after first steps this will save the arguments to the variables (it is necessary as I have had some problems in the implementation part), this step even coves any possible combination of the positions of the arguments (it does not matter now, if the amount is first or second; same for the path)
5. this is a very last step, which just (for any case) covers the case, the path included has not been inserted with the slash in front (as this is mandatory for bash to recognise it indicates an absolute path) or at the end (important for the touch command in the implementation path as the command looks like "touch -t *TIMESTAMP* "$PATHXfile$i""); together with it it does provide an appropriate action, if the path is defined with ".","..","./","../" - if there would be a full path, not relative one
if [[ $# -eq 2 ]]
then
if [[ $1 == ?(-)+([0-9]) ]] && [[ $2 == ?(-)+([0-9]) ]]
then
echo "Invalid arguments. Did you include a slash at the start of the path (if the file name consists only of the numbers)? Well, try it again. Terminating script..."
exit
elif [[ $1 == ?(-)+([a-z]) ]] && [[ $2 == ?(-)+([a-z]) ]]
then
echo "Only characters in the arguments. Is this some sort of a joke? If you have tried some sick hexadecimal format of number (just A-F), it will not work, mate. Terminating script..."
exit
fi
if [[ $1 == ?(-)+([0-9]) ]]
then
AMOUNT=$1
PATHX=$2
else
AMOUNT=$2
if [[ $AMOUNT != ?(-)+([0-9]) ]]
then
echo "No argument with number/numbers-only as an amount of files to create. Terminating script..."
exit
fi
PATHX=$1
fi
else
if [[ $# -eq 1 ]]
then
if [[ $1 == ?(-)+([0-9]) ]]
then
AMOUNT=$1
PATHX=$(pwd)
else
echo "Please, include the argument with an amount of files to create. Terminating script..."
exit
fi
else
echo "2 Arguments are expected. Terminating script..."
exit
fi
fi
if [[ $PATHX != /* ]]
then
if [[ "$PATHX" == "." ]] || [[ "$PATHX" == ".." ]] || [[ "$PATHX" == "./"* ]] || [[ "$PATHX" == "../"* ]]
then
true
else
PATHX=$(echo "/$PATHX")
fi
fi
if [[ $PATHX != */ ]]
then
PATHX=$(echo "$PATHX/")
fi
Excuse this formatting, but without adding the description to the code sample it is just a mess of text...
Anyway, thank you, Guys, for any input.
It may be reduced to:
[[ $# -gt 2 ]] && { echo "Incorrect number of arguments"; exit 1; }
[[ $1 == ?(-)+([0-9]) ]] || { echo "First argument is not a number"; exit 2; }
[[ $2 == ?(-)+([0-9]) ]] && { echo "Both arguments are numbers"; exit 3; }
[[ $2 == +([a-z]) ]] || { echo "File name must be only letters"; exit 4; }
[[ ! -d $2 ]] && { echo "Path does not exist"; exit 5; }
n=$1
p=${2:-$PWD} # Use the PWD if path is missing.
if [[ $p != */ ]]; then
echo "Missing trailing slash; correcting";
p+=/
fi
if [[ $p != /* ]]; then
echo "Missing leading slash; correcting";
[[ $p == #(.|..|./*|../*) ]] || p=/"$p"
fi

Bash Value Too Great For Base, cant use date in if statement

I have seen similar situations but couldn't really figure out how to correctly apply the suggested solutions to my situation.
I have a bash script with the following lines:
LAST=$(ssh root#host ls /backup3/mycomp/partition1/ | tail -1)
#get last backup dir (formatted YYYY-MM-DD/). If none exist then get yesterdays Date
if [[ -z "$LAST" || "$LAST" -eq "$TODAY" ]]
then
log "/backup3/$HOST/$NAME/ does not exist, probably first backup or second backup done today."
LAST="$YESTERDAY"
fi
When I run this, I get the following error:
[[: 2014-11-08: value too great for base (error token is "08")
because I am actually searching for physical directory names, I cant just remove the zero. How would I go about making this work?
Replace:
if [[ -z "$LAST" || "$LAST" -eq "$TODAY" ]]
With:
if [[ -z "$LAST" || "$LAST" == "$TODAY" ]]
Since dates are not valid numbers, you want to do string comparison (==) not numeric comparison (-eq).
Discussion
Observe:
$ [[ "10-1" -eq "9" ]] && echo True
True
$ [[ "6+3" -eq "9" ]] && echo True
True
In the numeric context, signaled -eq, the shell is doing arithmetic on the arguments. This means that, for your date 2014-11-08, the shell was taking 2014, subtracting 11, and then trying to subtract 08. The shell treats any number that begins with a 0 as octal. Since 08 is not a valid octal number, you received an error message.
Comparison of [ and [[
As Jonathan Leffler points out, the implied arithmetic feature of [[ is another subtle difference between the old [ and the newer [[. Observe:
$ [[ "6+3" -eq "9" ]] && echo True
True
$ [ "6+3" -eq "9" ] && echo True
bash: [: 6+3: integer expression expected
The implied arithmetic feature of [[ is not present in [.

Looping input command

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

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