Hi pls I'm new to shell scripting. I need to write a shell script that will ask for 2 parameters, one for startdate and another for number of days.
Then loop from start date to number of days and print each date.
E.g 16-09-2022 and 4 will print:
16-09-2022
17-09-2022
18-09-2022
19-09-2022
Tried this:
#! /bin/bash
echo "enter date"
read var1
echo "enter no of days"
read var2
for i in {0..$var2..1}
do
echo $i $var1
(here's where I'm trying to figure how to increment the dates)
done
Thanks
Try the following:
Save the script below in file, name it e.g. get-started-with-bash-by-example.sh and run it as follows:
bash get-started-with-bash-by-example.sh 2022-09-25 10
#!/bin/bash
# USAGE:
# ${SCRIPT_NAME} args
# arg1 - date (must be in format 'yyyy-mm-dd')
# arg2 - number of days to add
#
# EXAMPLES
# ${SCRIPT_NAME} 2022-09-25 10
for i in $(seq "$2")
do
date +%d-%m-%Y -d "$1 + $i day"
done
Or with user input:
#!/bin/bash
read -p "Enter start-date in 'yyyy-mm-dd' format: " startdate
read -p "Enter number of days to add: " nofdaystoadd
for i in $(seq "$nofdaystoadd")
do
date +%d-%m-%Y -d "$startdate + $i day"
done
OUTPUT:
26-09-2022
27-09-2022
28-09-2022
29-09-2022
30-09-2022
01-10-2022
02-10-2022
03-10-2022
04-10-2022
05-10-2022
I hope this example helps you to get started with bash.
Related
I have a little problem here. I am trying to write a script shell that shows background processes, but at a certain date. The date is a positional parameter. I've searched on Internet about the date validation, but all the info I found got me confused. I'm fairly new at this so a little help would be appreciated.
How do I verify if the date parameter is in a valid format in my shell script?
you can use regex :
#! /bin/bash
isDateInvalid()
{
DATE="${1}"
# Autorized separator char ['space', '/', '.', '_', '-']
SEPAR="([ \/._-])?"
# Date format day[01..31], month[01,03,05,07,08,10,12], year[1900..2099]
DATE_1="((([123][0]|[012][1-9])|3[1])${SEPAR}(0[13578]|1[02])${SEPAR}(19|20)[0-9][0-9])"
# Date format day[01..30], month[04,06,09,11], year[1900..2099]
DATE_2="(([123][0]|[012][1-9])${SEPAR}(0[469]|11)${SEPAR}(19|20)[0-9][0-9])"
# Date format day[01..28], month[02], year[1900..2099]
DATE_3="(([12][0]|[01][1-9]|2[1-8])${SEPAR}02${SEPAR}(19|20)[0-9][0-9])"
# Date format day[29], month[02], year[1904..2096]
DATE_4="(29${SEPAR}02${SEPAR}(19|20(0[48]|[2468][048]|[13579][26])))"
# Match the date in the Regex
if ! [[ "${DATE}" =~ "^(${DATE_1}|${DATE_2}|${DATE_3}|${DATE_4})$" ]]
then
echo -e "ERROR - '${DATE}' invalid!"
else
echo "${DATE} is valid"
fi
}
echo
echo "Exp 1: "`isDateInvalid '12/13/3550'`
echo "Exp 2: "`isDateInvalid '12/11/20322'`
echo "Exp 3: "`isDateInvalid '12 01 2000'`
echo "Exp 4: "`isDateInvalid '28-02-2014'`
echo "Exp 5: "`isDateInvalid '12_02_2002'`
echo "Exp 6: "`isDateInvalid '12.10.2099'`
echo "Exp 7: "`isDateInvalid '31/11/2020'`
If I'm understanding correctly, you want to input a date and filter background processes by that input and you are looking for a way to validate the input using the shell?
If you can break out the year/month/day into a slash-separated format, you can use the date command to test if the date stamp is valid. This runs under bash, other shells may be different
bad
$ echo -n "year :"; read year; echo -n "month :"; read month; echo -n "day :"; read day; date -d "$year/$month/$day" || echo "Not a valid date"
year :9999
month :32
day :32
date: invalid date ‘9999/32/32’
Not a valid date
good
$ echo -n "year :"; read year; echo -n "month :"; read month; echo -n "day :"; read day; date -d "$year/$month/$day" || echo "Not a valid date"
year :2020
month :3
day :23
Mon Mar 23 00:00:00 CDT 2020
I am writing a bash shell script in Linux, this program will accept a date 08-FEB-18 11.45.18.844 AM as a parameter.
I am wondering if there is a simply way to check if the date time is valid?
You can get a bit creative since you have bash and map the date string into an array which can then be easily parsed with date -d (and the help of another associative array). Once the date/time is mapped to array elements and converted to seconds since epoch with date -d, you simply check the return of the date command to determine if the conversion succeeded or failed. Handle the return appropriately:
#!/bin/bash
[ -n "$1" ] || { ## validate one argument given
printf "error: insufficient input\nusage: %s dd-mmm-yy hh.mm.ss.ms\n" \
"${0##*/}"
exit 1
}
oifs="$IFS" ## save original Internal Field Separator
IFS=$' \t\n-.'; ## set IFS to break on - or .
dt=( $(echo $1) ) ## separate date into indexed array
[ "${#dt[#]}" -lt '7' ] && { ## check all 7 components present
printf "error: date doesn't match dd-mmm-yy hh.mm.ss.ms format\n"
exit 1
}
IFS="$oifs" ## reset original IFS
## create associative array mapping months to numerics
declare -A mo=(
[JAN]=1
[FEB]=2
[MAR]=3
[APR]=4
[MAY]=5
[JUN]=6
[JUL]=7
[AUG]=8
[SEP]=9
[OCT]=10
[NOV]=11
[DEC]=12
)
## any date after 30 considerd 1930, else considered 2000
[ "${dt[2]}" -gt '30' ] && dt[2]=$((${dt[2]} + 1000)) || \
dt[2]=$((${dt[2]} + 2000))
## use date to convert array contents to seconds since epoch
epochsec=$( date -d "${dt[2]}-${mo[${dt[1]}]}-${dt[0]} \
${dt[3]}:${dt[4]}:${dt[5]}.${dt[6]}" +%s )
if [ "$?" -ne '0' ]; then ## check if last return was error
printf "error: invalid date.\n"
else ## output good date
printf "date: %s\n" "$(date -d #$epochsec)"
fi
Example Use/Output
$ bash chkcustomdt.sh "08-FEB-18 11.45.18.844"
date: Thu Feb 8 11:45:18 CST 2018
There are a lot of ways to approach this, this was just the first that came to mind.
So I'm new to bash and I have to make a script that include dynamically echoing lines with changing timestamps HH:MM.
So when I give say
sh run.sh 03:40 05:40
It should echo all the times between the given range
Ex: 03:31 03:32 ........ 05:39 05:40
I know it really simple with loops but I'm not able to figure it out.Any Help?
I have this not so good code which doesnt work as of now.
echo "Enter from Hour:"
read fromhr
echo "Enter from Min:"
read frommin
echo "Enter to Hour:"
read tohr
echo "Enter to Min:"
read tomin
while [ $fromhr -le $tohr ]; do
while [ $frommin -le $tomin ]; do
echo "$fromhr:$frommin"
if [ $frommin -eq 60 ]; then
frommin=0
break
fi
((frommin++))
done
if [ $fromhr -eq 24 ]; then
fromhr=0
fi
((fromhr++))
done
Example 1: Use bash only, faster:
#!/bin/bash
# - input data
fh=03 # from hour
th=05 # to hour
fm=30 # from minute
tm=30 # to minute
for ((h=fh;h<=th;h++)); do
for ((m=0;m<=59;m++)); do
[[ $h -le $fh && $m -lt $fm ]] && continue
[[ $h -ge $th && $m -gt $tm ]] && break
printf '%02d:%02d\n' $h $m
done
done
Example 2: use date to convert back and forth, shorter code, but much slower:
#!/bin/bash
# 1) input data
ft='03:30' # from time
tt='05:30' # to time
# 2) convert to Epochtime (second)
f=`date +%s -d "$ft"` # from
t=`date +%s -d "$tt"` # to
for ((s=f;s<=t;s+=60)); do # 60 seconds = 1 minute
date +%H:%M -d #$s # convert from Epochtime to H:M
done
Note that if you're comparing that from is less than to, you're unlikely to ever reach the hour/date change. Say, iterating from 20:00 to 05:00 won't even happen; and if you iterate from 12:38 to 17:12, there won't be any minutes changed (the inner loop's condition is instantly false). Few steps are suggested.
Change each condition's operator to -ne' rather than-le'.
Move both increments (frommin++ and fromhr++) BEFORE the respective overflow checks (otherwise you will constantly see 24 hours and 60 minutes in the output).
Try this and see if you want to beautify it even more.
Sample code:
#!/bin/bash
# Convert the given start/end time to seconds
# Replace time string with required HH:MM value
start_t=`date -d "03:30" +%s`
end_t=`date -d "03:33" +%s`
while [ ${start_t} -le ${end_t} ]; do
# Print time in HH:MM format
date -d #${start_t} +"%H:%M"
# Increment minute part
start_t=$(expr ${start_t} + 60)
done
I'm newbie in bash and need some advice.
I have a .txt file with a time stamp inside that is reloaded every x time, and each time stamps the current date and time.
"20221218-0841"
Now i have build a bash script to check the content and give me an answer if it is the same.
#!/bin/bash
time_status=`cat /root/test.txt | tail -c 14 | cut -d')' -f1`
date_now=`date +%Y%m%d-%H%M`
if [ "$date_now" == "$time_status" ]
then
echo "OK - $time_status "
date +%Y%m%d-%H%M
exit 0
fi
if [ "$date_now" != "$time_status" ]
then
echo "WARNING - $time_status "
date +%Y%m%d-%H%M
exit 1
fi
Everything is ok since now, the script does what it have to do, but i need to get ok for answer and exit with 0 when the time is ± 3 min not exactly the same.
Can someone provide some leads into this?
You can manipulate the date, this way,
# Reading only the '%H%M' part from two variables using read and spitting
# with '-' de-limiter
IFS='-' read _ hourMinuteFromFile <<<"$time_status"
IFS='-' read _ currentHourMinute <<<"$date_now"
# Getting the diff only for the minutes field which form the last two
# parts of the variable above
dateDiff=$(( ${hourMinuteFromFile: -2} - ${currentHourMinute: -2} ))
# Having the condition now for the difference from -3 to 3 as below,
if (( -3 <= ${dateDiff} <=3 )); then
echo "OK - $time_status "
fi
Dry run,
time_status="20170318-1438"
date_now="20170318-1436"
dateDiff=$(( ${hourMinuteFromFile: -2} - ${currentHourMinute: -2} ))
echo "$dateDiff"
2
Another good coding practice is to avoid using ``, back-ticks for command-substitution and use ${..} syntax and also do-away with a the useless use of cat,
time_status=$(tail -c 14 file | cut -d')' -f1)
date_now=$(date +%Y%m%d-%H%M)
You can transform the dates into seconds since 1970-01-01 00:00:00 UTC with date +%s and then perform the usual integer arithmetic on the result.
d1='2017-03-18 10:39:34'
d2='2017-03-18 10:42:25'
s1=$(date +%s -d "$d1")
s2=$(date +%s -d "$d2")
ds=$((s1 - s2))
if [ "$ds" -ge -180 -a "$ds" -le 180 ]
then
echo same
else
echo different
fi
I am trying to create a list of dates. This script below works with simple dates with no spaces.
datestart=20130601
dateend=20130705
for (( date1="$datestart"; date1 != dateend; )); do
date1="$(date --date="$date1 + 1 days" +'%Y%m%d')";
echo $date1;
done
When I use a data string like (which contains WHITE SPACE) datestart="2013-06-01 00:00:00" and a date format like +'Y-%m-%d %H:%M:%S'
datestart="2013-06-01 00:00:00"
dateend="2013-07-05 00:00:00"
for (( date1="$datestart"; date1 != "$dateend"; )); do
date1="$(date --date=""$date1" + 1 days" +'%Y-%d-%m %H:%M:%S')";
echo "$date1";
done
I get the following error:
-bash: ((: date1=2013-06-01 00:00:00: syntax error in expression (error token is "00:00:00")
I think I am NOT quoting my variables correctly. I have twiddled and fiddled, and now I am here. How do I quote the variables correctly in a for loop?
This should work for you:
datestart="2013-06-01 00:00:00"
dateend="2013-07-05 00:00:00"
date1="$datestart"
while [[ "$date1" != "$dateend" ]]; do
date1="$(date -u --date="$date1 tomorrow" '+%Y-%m-%d %H:%M:%S')"
echo "$date1"
done
Working Demo
((...)) is used for arithmetic operations only. Use while loop instead.
No need to use nested quotes for $date
Use tomorrow to get next date
Use correct year-month-date format while assigning next date to date1
No way, bash three-expression for loop expects aritmetic expressions. See this link:
http://wiki.bash-hackers.org/syntax/ccmd/c_for
use a classic "while" loop instead.
Addendum
You can use seconds from epoch to allow arithmetics, like in:
datestart=$(date --date="2013-06-01 00:00:00" +%s)
dateend=$(date --date="2013-07-05 00:00:00" +%s)
for (( date1=$datestart; date1 != $dateend; date1+=86400 )); do
date --date=#$date1
done
but care with days that has not 86400 seconds.
You're right about your quotes being off. You had:
date1="$(date --date=""$date1" + 1 days" +'%Y-%d-%m %H:%M:%S')";
but you're doing in and out of your double quotes in very weird ways with this. A quick solution might be to switch to single quotes, which do not isolate variables if they are inside double quotes:
date1="$(date --date="'$date1' + 1 days" +'%Y-%d-%m %H:%M:%S')";
As for the other part ... let's just review what's on the bash man page:
for (( expr1 ; expr2 ; expr3 )) ; do list ; done
First, the arithmetic expression expr1 is evaluated according to
the rules described below under ARITHMETIC EVALUATION. ...
And if you check the ARITHMETIC EVALUATION section of the man page, you'll see that it does not include the sorts of tests that /bin/test or [[ ... ]] can run. Those are covered in the next section of the man page, CONDITIONAL EXPRESSIONS.
If you want to use a for loop, then #pasaba's suggestion to use epoch seconds is what I'd go with also, in order to stick with arithmetic. Something like this:
#!/bin/bash
datestart="2013-06-01 00:00:00"
dateend="2013-07-05 00:00:00"
e_start=$(date -d "$datestart" '+%s')
e_end=$(date -d "$dateend" '+%s')
for (( date1=$e_start; date1 < $e_end; date1+=86400 )); do
echo -n "$date1 "; date -d "#$date1" '+%Y-%m-%d'
done
To account for leap years and leap seconds and the like, you can put your trust in the Linux date command, and evaluate $date1 in the loop as you originally did:
for (( date1=$e_start; date1 < $e_end; )); do
date1=$(date -d "$(date -d "#$date1") + 1 day" '+%s')
echo -n "$date1 "; date -d "#$date1" '+%Y-%m-%d'
done
The nested date commands are required because Linux's date command doesn't allow you to use relative dates ("+1day" or "tomorrow") when the origin date is specified as an epoch with #. (I'd love to know if I'm wrong about that.)
I realize that your question is tagged "Linux", but I'll note for future searches that this is a non-portable (Linux-only) use of the date command, so if you want this script to run in FreeBSD, NetBSD, OSX, etc, you'll need to review their usage. The following works in FreeBSD:
#!/usr/bin/env bash
datestart="2015-06-01 00:00:00"
dateend="2015-07-05 00:00:00"
e_start="$(date -jf '%Y-%m-%d %T' "$datestart" '+%s')"
e_end="$(date -jf '%Y-%m-%d %T' "$dateend" '+%s')"
for (( date1 = $e_start; date1 < $e_end; )); do
date1=$(date -j -v+1d -f '%s' "$date1" '+%s')
echo -n "$date1 "; date -jf '%s' "$date1" '+%Y-%m-%d'
done