Related
I am dealing with sorting words in Bash according to a given argument. I am given either argument -r, -a , -v or -h and according to it there are options to sort the words, as you can see at my "help".
Somehow, if I pass the argument -r it creates an error. I really don't understand what I am doing wrong, as if[["$arg"=="-a"]] works, but I have to use case somehow.
Here is my code:
#!/bin/bash
# Natalie Zubkova , zubkonat
# zubkonat#cvut.fel.cz , LS
#help
help="This script will calculate occurances of words in a given file, and it will sort them according to the given argument in following order> \n
without parametre = increasing order according to a number of occurance\n
-r = decreasing order according to a number of occurance\n
-a = in alphabetical increasing order\n
-a -r = in alphabetical decreasing order\n
There are also special cases of the given parametre, when the script is not sorting but:\n
-h = for obtaining help \n
-v = for obtaining a number of this task "
# this function will divide a given chain into a words, so we can start calculating the occurances, we also convert all the capital letters to the small ones by - tr
a=0;
r=0;
EXT=0;
if [ "$1" == "-h" ]; then
echo $help
exit 0
fi
if [ "$2" == "-h" ]; then
echo $help
exit 0
fi
if [ "$1" == "-v" ]; then
echo "5"
exit 0
fi
if [ "$2" == "-v" ]; then
echo "5"
exit 0
fi
function swap {
while read x y; do
echo "$y" "$x";
done
}
function clearAll {
sed -e 's/[^a-z]/\n/gI' | tr '[A-Z]' '[a-z]' | sort | uniq -c |awk '{i++; if(i!=1) print $2" "$1}' #swap
}
for arg do
case "$arg" in
"-a")
a=1
;;
"-r")
r=1
;;
"-v")
echo "5" #number of task is 5
exit 0
;;
"-h")
echo $help
exit 0
;;
"-?")
echo "invalid parametre, please display a help using argument h"
exit 0
;;
esac
done
#Sort according to parametres -a and -r
function sortWords {
if [[ a -eq 1 ]]; then
if [[ r -eq 0 ]]; then
clearAll | sort -nk1
fi
fi
if [[ a -eq 1 ]]; then
if [[ r -eq 1 ]]; then
clearAll | sort -nk1 -r
fi
fi
if [[ r -eq 1 ]]; then
if [[ a -eq 0 ]]; then
clearAll | sort -nk2 -r
fi
fi
if [[ a -eq 0 ]]; then
if [[ r -eq 0 ]]; then
clearAll | sort -nk2
fi
fi
}
#code is from Stackoverflow.com
function cat-all {
while IFS= read -r file
do
if [[ ! -z "$file" ]]; then
cat "$file"
fi
done
}
#histogram
hist=""
for arg do
if [[ ! -e "$arg" ]]; then
EXT=1;
echo "A FILE DOESNT EXIST" >&2
continue;
elif [[ ! -f "$arg" ]]; then
EXT=1;
echo "A FILE DOESNT EXIST" >&2
continue;
elif [[ ! -r "$arg" ]]; then
EXT=1;
echo "A FILE DOESNT EXIST" >&2
continue;
fi
done
for arg do
hist="$hist""$arg""\n"
done
echo -e "$hist" | cat-all | sortWords
exit $EXT;
Here is what our upload system which does some test to see if our program works says:
Test #6
> b5.sh -r ./easy.txt
ERROR: script output is wrong:
--- expected output
+++ script stdout
## --- line 1 (167 lines) ; +++ no lines ##
-the 89
-steam 46
-a 39
-of 37
-to 35
...
script written 484 lines, while 484 lines are expected
script error output:
A FILE DOESNT EXIST
cat: invalid option -- 'r'
Try `cat --help' for more information.
script exit value: 1
ERROR: Interrupted due to failed test
If anyone could help me I would really appreciate it.
You forgot to move the parameter index position with shift:
"-r")
r=1
shift
;;
shift above moves to the next command line arg: ./easy.txt in your case.
Without it, read -r file will read -r instead of the file name.
I have to create a Shell Script wherein one of the parameters will be the date in the format dd/mm/yyyy. My question is, how can I check if the Date passed as parameter really follows this Date Format? I tried to use the grep command as below:
if echo "$1" | grep -q '^[0-3][0-9]/[0-1][0-9]/[0-9]\{4\}$'
but it didn't give the correct format because the day for example can be 33, 34, (...), that is not really the correct format. Anyone know something that can really check if the date passed really follows the format dd/mm/yyyy ?
Use date
date "+%d/%m/%Y" -d "09/99/2013" > /dev/null 2>&1
is_valid=$?
The date string must be in "MM/DD/YYYY" format.
If you do not get 0 then date is in invalid format.
The simplest solution, that still works perfectly, is the following :
if [[ $1 =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] && date -d "$1" >/dev/null 2>&1
...
It consists in combining 2 checks :
the first part checks that $1 is of this format : NNNN-NN-NN
the second part checks that it is a valid date
You need the two checks because :
if you don't do the first check, date will exit with code 0 even if your variable is a valid date in another format
if you don't do the second check, then you can end up with a 0 even for variables such as 2016-13-45
This function expects 2 strings,a format string, a date string
The format string uses the codes from the date command but does not include the '+'
The function returns 0 if the provided date matches the given format, otherwise it returns 1
Code (my_script.sh)
#!/bin/bash
datecheck() {
local format="$1" d="$2"
[[ "$(date "+$format" -d "$d" 2>/dev/null)" == "$d" ]]
}
date_test="$1"
echo $date_test
if datecheck "%d %b %Y" "$date_test"; then
echo OK
else
echo KO
fi
Output
$ ./my_script.sh "05 Apr 2020"
05 Apr 2020
OK
$ ./my_script.sh "foo bar"
foo bar
KO
First, check the form of the input using the regex. Then use awk to switch to mm/dd/yyyy and use date to validate. You can use the following expression in your if statement:
echo "$1" | egrep -q '^[0-3][0-9]/[0-1][0-9]/[0-9]{4}$' && date -d "$(echo "$1" | awk 'BEGIN{FS=OFS="/"}{print $2"/"$1"/"$3}')" >/dev/null 2>&1
Simplest way for dd/mm/yyyy exactly in Bash is:
if [[ $1 == [0-3][0-9]/[0-1][0-9]/[0-9][0-9][0-9][0-9] ]]
Or
if [[ $1 =~ ^[0-3][0-9]/[0-1][0-9]/[0-9]{4}$ ]]
How about using awk:
echo "31/12/1999" | awk -F '/' '{ print ($1 <= 31 && $2 <= 12 && match($3, /^[1-9][1-9][1-9][1-9]$/)) ? "good" : "bad" }'
It prints "good" if its valid date else prints "bad"
#! /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/3000'`
echo "Exp 2: "`isDateInvalid '12/11/2014'`
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/2000'`
Here's a function to do some data validation this:
# Script expecting a Date parameter in MM-DD-YYYY format as input
verifyInputDate(){
echo ${date} | grep '^[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]$'
if [ $? -eq 0 ]; then
echo "Date is valid"
else
echo "Date is not valid"
fi
}
`X="2016-04-21" then check for the below value being 1 or 0.
cal echo $x | cut -c 6-7 echo $x | cut -c 1-4 2>/dev/null | grep -c echo $x | cut -c 9-10
If the value is 1, then it's valid, else it's not valid.
Though the solution (if [[ $1 =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] && date -d "$1" >/dev/null 2>&1) of #https://stackoverflow.com/users/2873507/vic-seedoubleyew is best one at least for linux, but it gives error as we can not directly compare/match regex in if statement. We should put the regex in a variable and then we should compare/match that variable in if statement. Moreover second part of if condition does not return a boolean value so this part will also cause error.
So I have done slight modification in the above formula and this modification can also be customized further for various other formats or combination of them.
DATEVALUE=2019-11-12
REGEX='^[0-9]{4}-[0-9]{2}-[0-9]{2}$'
if [[ $DATEVALUE =~ $REGEX ]] ; then
date -d $DATEVALUE
if [ $? -eq 0 ] ; then
echo "RIGHT DATE"
else
echo "WRONG DATE"
fi
else
echo "WRONG FORMAT"
fi
I would like to give an extended answer for a slightly different format, but this can easily be changed to the dd/mm/YY format with the answers already given; it's tested on busybox (posix shell)
This is one of the first hits for web searches similar to "busybox posix shell script date" and "test format" or "validate" etc, so here my solution for busybox (tested with 1.29.3, 1.23.1)
#!/bin/sh
##########
#
# check if date valid in busybox
# tested in busybox 1.29.3, 1.23.1
#
# call with:
# $0 <yyyymmdd>
#
##########
mydate=$1
if echo $mydate | grep -qE '20[0-9][0-9](0[1-9]|1[0-2])([012][0-9]|3[01])'; then
printf 'may be valid\n'
date +%Y%m%d -d $mydate -D %Y%m%d > /dev/null 2>&1
is_valid=$?
if [ $is_valid -ne 0 ]; then
printf 'not valid\n'
return 1
else
mytestdate=$(date +%Y%m%d -d $mydate -D %Y%m%d)
if [ $mydate -ne $mytestdate ]; then
printf 'not valid, results in "%s"\n' "$mytestdate"
return 1
else
printf 'valid\n'
fi
fi
else
printf 'not valid (must be: <yyyymmdd>)\n'
return 1
fi
as in busybox (1.29.3 & 1.23.1) you have responds like:
lxsys:~# date +%Y%m%d -d 20110229 -D "%Y%m%d"
20110301
I had the need to validate the date in some better way but i wanted to rely mostly on the system itself
so with
mytestdate=$(date +%Y%m%d -d $mydate -D %Y%m%d)
if [ $mydate -ne $mytestdate ]; then
...
fi
there is a second test - do we have a difference between the wanted or given format (input, $mydate) and the system interpretation (output, $mytestdate) of it ... if it's not the same, discard the date
I wrote this bash script to validate date. I can accept mont as alphanumeric.
#!/bin/bash
function isDateValid {
DATE=$1
if [[ $DATE =~ ^[0-9]{1,2}-[0-9a-zA-Z]{1,3}-[0-9]{4}$ ]]; then
echo "Date $DATE is a number!"
day=`echo $DATE | cut -d'-' -f1`
month=`echo $DATE | cut -d'-' -f2`
year=`echo $DATE | cut -d'-' -f3`
if [ "$month" == "01" ] || [ "$month" == "1" ]; then
month="Jan"
elif [ "$month" == "02" ] || [ "$month" == "2" ]; then
month="Feb"
elif [ "$month" == "03" ] || [ "$month" == "3" ]; then
month="Mar"
elif [ "$month" == "04" ] || [ "$month" == "4" ]; then
month="Apr"
elif [ "$month" == "05" ] || [ "$month" == "5" ]; then
month="May"
elif [ "$month" == "06" ] || [ "$month" == "6" ]; then
month="Jun"
elif [ "$month" == "07" ] || [ "$month" == "7" ]; then
month="Jul"
elif [ "$month" == "08" ] || [ "$month" == "8" ]; then
month="Aug"
elif [ "$month" == "09" ] || [ "$month" == "9" ]; then
month="Sep"
elif [ "$month" == "10" ]; then
month="Oct"
elif [ "$month" == "11" ]; then
month="Nov"
elif [ "$month" == "12" ]; then
month="Dec"
fi
ymd=$year"-"$month"-"$day
echo "ymd: "$ymd
dmy=$(echo "$ymd" | awk -F- '{ OFS=FS; print $3,$2,$1 }')
echo "dmy: "$dmy
if date --date "$dmy" >/dev/null 2>&1; then
echo "OK"
return 0
else
echo "NOK"
return 1
fi
else
echo "Date $DATE is not a number"
return 1
fi
}
if isDateValid "15-15-2014"; then
echo "date is valid =)"
else
echo "bad format date"
fi
echo "==================="
if isDateValid "15-12-2014"; then
echo "date is valid =)"
else
echo "bad format date"
fi
echo "==================="
if isDateValid "15-Dec-2014"; then
echo "date is valid =)"
else
echo "bad format date"
fi
echo "==================="
if isDateValid "1-May-2014"; then
echo "date is valid =)"
else
echo "bad format date"
fi
echo "==================="
if isDateValid "1-1-2014"; then
echo "date is valid =)"
else
echo "bad format date"
fi
echo "==================="
if isDateValid "12-12-2014"; then
echo "date is valid =)"
else
echo "bad format date"
fi
Blockquote
DATE = "$*"
[[ "${DATE}" != #(((([123][0]|[012][1-9])|3[1])?([ \/._-])(0[13578]|1[02])?([ \/._-])(19|20)[0-9][0-9])|(([123][0]|[012][1-9])?([ \/._-])\
(0[469]|11)?([ \/._-])(19|20)[0-9][0-9])|(([12][0]|[01][1-9]|2[1-8])?([ \/._-])02?([ \/._-])(19|20)[0-9][0-9])|(29?([ \/._-])02?([ \/._-])\
(19|20(0[48]|[2468][048]|[13579][26])))) ]] && echo error || echo good)
I need a shell script program to print the hexadecimal number from big endian to little endian
For example
Input: my virtual address = 00d66d7e
Output: 7e6dd600
How can I can I create this in a bash script?
Just had to do this... but from decimal to little endian.. adapting that here:
echo 00d66d7e | tac -rs .. | echo "$(tr -d '\n')"
achieves the desired result, for arbitrarily sized hexadecimal representations of unsigned integers.
(h/t 'tac -rs' MestreLion, very nice!)
For 32 bit addresses, assuming it's zero padded:
v=00d66d7e
echo ${v:6:2}${v:4:2}${v:2:2}${v:0:2}
# 7e6dd600
Based on Karoly's answer you could use the following script, reading an argument or piped input:
#!/bin/bash
# check 1st arg or stdin
if [ $# -ne 1 ]; then
if [ -t 0 ]; then
exit
else
v=`cat /dev/stdin`
fi
else
v=$1
fi
i=${#v}
while [ $i -gt 0 ]
do
i=$[$i-2]
echo -n ${v:$i:2}
done
echo
For e.g. you could save this script as endian.sh and make it executable with:
chmod u+x endian.sh
Then:
echo 00d66d7e | ./endian.sh
gives you:
7e6dd600
For a different length string:
echo d76f411475428afc90947ee320 | ./endian.sh
result would be:
20e37e9490fc8a427514416fd7
#Update: Modified the script to accept the input either as an argument or from stdin, addressing Freewind's request. So now:
./endian.sh d76f411475428afc90947ee320
also works and gives you:
20e37e9490fc8a427514416fd7
This works for dash (and many other shells) :
v=0x12345678
v2=$(( (v<<8 & 0xff00ff00) | (v>>8 & 0xff00ff) ))
v2=$(( (v2<<16 & 0xffff0000) | v2>>16 ))
printf '0x%08x\n' $v2
Result should be "0x78563412"
${v:6:2} is for bash.
In response to Freewind's comment request and building off of hutheano's great answer, I wrote my own bash script and I include a condensed version below. The full script can be downloaded here.
The following implementation accounts for odd length strings, 0x or \x prefixes, and multiple output formats and can be used like the following:
$ be2le d76f411475428afc90947ee320 0xaaff 0xffa '\x3'
20e37e9490fc8a427514416fd7
0xffaa
0xfa0f
\x03
be2le bash script
#!/bin/bash
args=()
format=preserve
delimiter="\n"
nonewline=false
join=false
strip=false
while (( "$#" )); do
case "$1" in
-h|--help) usage;;
-f) format=$2; shift 2;;
--format=*) format="${1#*=}"; shift;;
-d) delimiter=$2; shift 2;;
--delimiter=*) delimiter="${1#*=}"; shift;;
-n|--no-newline) nonewline=true; shift;;
-j|--join) join=true; shift;;
-s|--strip-null) strip=true; shift;;
-*|--*) echo "Error: unsupported flag $1 specified"; exit 1;;
*) args=( "${args[#]}" "$1" ); shift;;
esac
done
case "$format" in
preserve);;
int) prefix="0x";;
char) prefix="\x";;
raw) ;;
*) echo "Error: unsupported format $format"; exit 1;;
esac
n=0
parts=()
for arg in ${args[#]}; do
digest=""
prefix=""
# remove prefix if string begins with "0x"
if [[ $arg =~ ^[0\\]x ]]; then
if [ "$format" == "preserve" ]; then
prefix=${arg:0:2}
fi
arg=${arg:2}
fi
# zero-pad if string has odd length
if [ $[${#arg} % 2] != 0 ]; then
arg="0$arg"
fi
part=""
i=${#arg}
while [ $i -gt 0 ]; do
i=$[$i-2]
byte=${arg:$i:2}
if [ $strip == true ] && [ -z "$part" ] && [ $byte == "00" ]; then
continue
fi
case "$format" in
int) part="$part"'0x'"$byte ";;
char) part="$part\x$byte";;
raw) part="$part$(printf "%b" "\x$byte")";;
*) part="$part$byte";;
esac
done
digest="$prefix$digest$part"
parts=( "${parts[#]}" "$digest" )
n=$[$n+1]
done
if [ $join == true ]; then
case "$format" in
*) printf "%s" "${parts[#]}";;
esac
else
i=0
for part in "${parts[#]}"; do
if [[ $(($i + 1)) < $n ]]; then
printf "%s$delimiter" "$part"
else
printf "%s" "$part"
fi
i=$(($i+1))
done
fi
if [ $nonewline == false ]; then
echo
fi
This script is for flipping 16 bit data.
#!/bin/bash
if [ -t 0 ]; then exit; fi
data=`cat /dev/stdin | od -An -vtx1 | tr -d ' ' | tr -d '\n'`
length=${#data}
i=0
while [ $i -lt $length ]; do
echo -n -e "\x${data:$[$i+2]:2}"
echo -n -e "\x${data:$[$i]:2}"
i=$[$i+4]
done
I have to create a Shell Script wherein one of the parameters will be the date in the format dd/mm/yyyy. My question is, how can I check if the Date passed as parameter really follows this Date Format? I tried to use the grep command as below:
if echo "$1" | grep -q '^[0-3][0-9]/[0-1][0-9]/[0-9]\{4\}$'
but it didn't give the correct format because the day for example can be 33, 34, (...), that is not really the correct format. Anyone know something that can really check if the date passed really follows the format dd/mm/yyyy ?
Use date
date "+%d/%m/%Y" -d "09/99/2013" > /dev/null 2>&1
is_valid=$?
The date string must be in "MM/DD/YYYY" format.
If you do not get 0 then date is in invalid format.
The simplest solution, that still works perfectly, is the following :
if [[ $1 =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] && date -d "$1" >/dev/null 2>&1
...
It consists in combining 2 checks :
the first part checks that $1 is of this format : NNNN-NN-NN
the second part checks that it is a valid date
You need the two checks because :
if you don't do the first check, date will exit with code 0 even if your variable is a valid date in another format
if you don't do the second check, then you can end up with a 0 even for variables such as 2016-13-45
This function expects 2 strings,a format string, a date string
The format string uses the codes from the date command but does not include the '+'
The function returns 0 if the provided date matches the given format, otherwise it returns 1
Code (my_script.sh)
#!/bin/bash
datecheck() {
local format="$1" d="$2"
[[ "$(date "+$format" -d "$d" 2>/dev/null)" == "$d" ]]
}
date_test="$1"
echo $date_test
if datecheck "%d %b %Y" "$date_test"; then
echo OK
else
echo KO
fi
Output
$ ./my_script.sh "05 Apr 2020"
05 Apr 2020
OK
$ ./my_script.sh "foo bar"
foo bar
KO
First, check the form of the input using the regex. Then use awk to switch to mm/dd/yyyy and use date to validate. You can use the following expression in your if statement:
echo "$1" | egrep -q '^[0-3][0-9]/[0-1][0-9]/[0-9]{4}$' && date -d "$(echo "$1" | awk 'BEGIN{FS=OFS="/"}{print $2"/"$1"/"$3}')" >/dev/null 2>&1
Simplest way for dd/mm/yyyy exactly in Bash is:
if [[ $1 == [0-3][0-9]/[0-1][0-9]/[0-9][0-9][0-9][0-9] ]]
Or
if [[ $1 =~ ^[0-3][0-9]/[0-1][0-9]/[0-9]{4}$ ]]
How about using awk:
echo "31/12/1999" | awk -F '/' '{ print ($1 <= 31 && $2 <= 12 && match($3, /^[1-9][1-9][1-9][1-9]$/)) ? "good" : "bad" }'
It prints "good" if its valid date else prints "bad"
#! /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/3000'`
echo "Exp 2: "`isDateInvalid '12/11/2014'`
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/2000'`
Here's a function to do some data validation this:
# Script expecting a Date parameter in MM-DD-YYYY format as input
verifyInputDate(){
echo ${date} | grep '^[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]$'
if [ $? -eq 0 ]; then
echo "Date is valid"
else
echo "Date is not valid"
fi
}
`X="2016-04-21" then check for the below value being 1 or 0.
cal echo $x | cut -c 6-7 echo $x | cut -c 1-4 2>/dev/null | grep -c echo $x | cut -c 9-10
If the value is 1, then it's valid, else it's not valid.
Though the solution (if [[ $1 =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] && date -d "$1" >/dev/null 2>&1) of #https://stackoverflow.com/users/2873507/vic-seedoubleyew is best one at least for linux, but it gives error as we can not directly compare/match regex in if statement. We should put the regex in a variable and then we should compare/match that variable in if statement. Moreover second part of if condition does not return a boolean value so this part will also cause error.
So I have done slight modification in the above formula and this modification can also be customized further for various other formats or combination of them.
DATEVALUE=2019-11-12
REGEX='^[0-9]{4}-[0-9]{2}-[0-9]{2}$'
if [[ $DATEVALUE =~ $REGEX ]] ; then
date -d $DATEVALUE
if [ $? -eq 0 ] ; then
echo "RIGHT DATE"
else
echo "WRONG DATE"
fi
else
echo "WRONG FORMAT"
fi
I would like to give an extended answer for a slightly different format, but this can easily be changed to the dd/mm/YY format with the answers already given; it's tested on busybox (posix shell)
This is one of the first hits for web searches similar to "busybox posix shell script date" and "test format" or "validate" etc, so here my solution for busybox (tested with 1.29.3, 1.23.1)
#!/bin/sh
##########
#
# check if date valid in busybox
# tested in busybox 1.29.3, 1.23.1
#
# call with:
# $0 <yyyymmdd>
#
##########
mydate=$1
if echo $mydate | grep -qE '20[0-9][0-9](0[1-9]|1[0-2])([012][0-9]|3[01])'; then
printf 'may be valid\n'
date +%Y%m%d -d $mydate -D %Y%m%d > /dev/null 2>&1
is_valid=$?
if [ $is_valid -ne 0 ]; then
printf 'not valid\n'
return 1
else
mytestdate=$(date +%Y%m%d -d $mydate -D %Y%m%d)
if [ $mydate -ne $mytestdate ]; then
printf 'not valid, results in "%s"\n' "$mytestdate"
return 1
else
printf 'valid\n'
fi
fi
else
printf 'not valid (must be: <yyyymmdd>)\n'
return 1
fi
as in busybox (1.29.3 & 1.23.1) you have responds like:
lxsys:~# date +%Y%m%d -d 20110229 -D "%Y%m%d"
20110301
I had the need to validate the date in some better way but i wanted to rely mostly on the system itself
so with
mytestdate=$(date +%Y%m%d -d $mydate -D %Y%m%d)
if [ $mydate -ne $mytestdate ]; then
...
fi
there is a second test - do we have a difference between the wanted or given format (input, $mydate) and the system interpretation (output, $mytestdate) of it ... if it's not the same, discard the date
I wrote this bash script to validate date. I can accept mont as alphanumeric.
#!/bin/bash
function isDateValid {
DATE=$1
if [[ $DATE =~ ^[0-9]{1,2}-[0-9a-zA-Z]{1,3}-[0-9]{4}$ ]]; then
echo "Date $DATE is a number!"
day=`echo $DATE | cut -d'-' -f1`
month=`echo $DATE | cut -d'-' -f2`
year=`echo $DATE | cut -d'-' -f3`
if [ "$month" == "01" ] || [ "$month" == "1" ]; then
month="Jan"
elif [ "$month" == "02" ] || [ "$month" == "2" ]; then
month="Feb"
elif [ "$month" == "03" ] || [ "$month" == "3" ]; then
month="Mar"
elif [ "$month" == "04" ] || [ "$month" == "4" ]; then
month="Apr"
elif [ "$month" == "05" ] || [ "$month" == "5" ]; then
month="May"
elif [ "$month" == "06" ] || [ "$month" == "6" ]; then
month="Jun"
elif [ "$month" == "07" ] || [ "$month" == "7" ]; then
month="Jul"
elif [ "$month" == "08" ] || [ "$month" == "8" ]; then
month="Aug"
elif [ "$month" == "09" ] || [ "$month" == "9" ]; then
month="Sep"
elif [ "$month" == "10" ]; then
month="Oct"
elif [ "$month" == "11" ]; then
month="Nov"
elif [ "$month" == "12" ]; then
month="Dec"
fi
ymd=$year"-"$month"-"$day
echo "ymd: "$ymd
dmy=$(echo "$ymd" | awk -F- '{ OFS=FS; print $3,$2,$1 }')
echo "dmy: "$dmy
if date --date "$dmy" >/dev/null 2>&1; then
echo "OK"
return 0
else
echo "NOK"
return 1
fi
else
echo "Date $DATE is not a number"
return 1
fi
}
if isDateValid "15-15-2014"; then
echo "date is valid =)"
else
echo "bad format date"
fi
echo "==================="
if isDateValid "15-12-2014"; then
echo "date is valid =)"
else
echo "bad format date"
fi
echo "==================="
if isDateValid "15-Dec-2014"; then
echo "date is valid =)"
else
echo "bad format date"
fi
echo "==================="
if isDateValid "1-May-2014"; then
echo "date is valid =)"
else
echo "bad format date"
fi
echo "==================="
if isDateValid "1-1-2014"; then
echo "date is valid =)"
else
echo "bad format date"
fi
echo "==================="
if isDateValid "12-12-2014"; then
echo "date is valid =)"
else
echo "bad format date"
fi
Blockquote
DATE = "$*"
[[ "${DATE}" != #(((([123][0]|[012][1-9])|3[1])?([ \/._-])(0[13578]|1[02])?([ \/._-])(19|20)[0-9][0-9])|(([123][0]|[012][1-9])?([ \/._-])\
(0[469]|11)?([ \/._-])(19|20)[0-9][0-9])|(([12][0]|[01][1-9]|2[1-8])?([ \/._-])02?([ \/._-])(19|20)[0-9][0-9])|(29?([ \/._-])02?([ \/._-])\
(19|20(0[48]|[2468][048]|[13579][26])))) ]] && echo error || echo good)
In BASH shell scripting or using gdate, given a date like "Oct 2011" how do I convert to a year-month number format? Output should be "2011-10", for example.
mydate="Oct 2011"
date --date="$(printf "01 %s" $mydate)" +"%Y-%m"
The parse_datetime interface for GNU date (which is what the example uses) has lots of rules. the Oct 2011 form of the date isn't one of them, so you prepend a "01 " to the front of it and date likes it.
read mon year <<< "Oct 2012"
date -d "$mon 1 $year" "+%Y-%m"
Result:
2012-10
You can convert the month to a number by finding the position of the name string:
#!/bin/bash
month=Oct
months="JanFebMarAprMayJunJulAugSepOctNovDec"
tmp=${months%%$month*}
month=${#tmp}
monthnumber $((month/3+1))
printf "%02d\n" $monthnumber
The output of the script above is:
10
Your specific string you could code:
#!/bin/bash
mydate="Oct 2011"
monthnumber() {
month=$1
months="JanFebMarAprMayJunJulAugSepOctNovDec"
tmp=${months%%$month*}
month=${#tmp}
monthnumber=$((month/3+1))
printf "%02d\n" $monthnumber
}
arr=(`echo ${mydate}`);
month=$(monthnumber ${arr[0]})
year=$(echo ${arr[1]})
echo "$year-$month"
The output would be:
2011-10
case "`date | awk '{print $2 }'`" in
Jan) MON="01" ;;
Feb) MON="02" ;;
Mar) MON="03" ;;
Apr) MON="04" ;;
May) MON="05" ;;
Jun) MON="06" ;;
Jul) MON="07" ;;
Aug) MON="08" ;;
Sep) MON="09" ;;
Oct) MON="10" ;;
Nov) MON="11" ;;
Dec) MON="12" ;;
esac
echo $MON
I'm not sure if there is a shorter way of doing this, but here is one way. This is by no means fool proof. You can improve this by adding other checks to input and make the comparison case insensitive.
#!/bin/ksh
### Validate input
if [ $# -eq 0 ]
then
echo "Usage: $0 InputMonYYYY"
echo "Example: $0 \"Oct 2011\""
exit 1
fi
### Read input
INPUTSTR=$1
MON_STR=`echo $INPUTSTR |cut -d' ' -f1`
YYYY_STR=`echo $INPUTSTR |cut -d' ' -f2`
if [[ "$MON_STR" = "Jan" ]] then
MON_NUM=01
elif [[ "$MON_STR" = "Feb" ]] then
MON_NUM=02
elif [[ "$MON_STR" = "Mar" ]] then
MON_NUM=03
elif [[ "$MON_STR" = "Apr" ]] then
MON_NUM=04
elif [[ "$MON_STR" = "May" ]] then
MON_NUM=05
elif [[ "$MON_STR" = "Jun" ]] then
MON_NUM=06
elif [[ "$MON_STR" = "Jul" ]] then
MON_NUM=07
elif [[ "$MON_STR" = "Aug" ]] then
MON_NUM=08
elif [[ "$MON_STR" = "Sep" ]] then
MON_NUM=09
elif [[ "$MON_STR" = "Oct" ]] then
MON_NUM=10
elif [[ "$MON_STR" = "Nov" ]] then
MON_NUM=11
elif [[ "$MON_STR" = "Dec" ]] then
MON_NUM=12
fi
echo ${YYYY_STR}-${MON_NUM}
Bash4 supports hash-tables (answer by Jim is the correct one though).
Example
#!/bin/bash
declare -A months=( ["Jan"]="01" ["Feb"]="02" )
mydate="Jan 2011"
echo ${mydate:4:8}-"${months["${mydate:0:3}"]}"
Output:
2011-01
Let's kick this dead horse.
If you don't care about invalid month names you can use this function I've written which is quite short (and only does 1 exec) but expects a month to be valid english 3-chars lower or upper case and only requires GNU sed and bash:
m2n() { echo $((-10+$(sed 's/./\U&/g;y/ABCEGLNOPRTUVY/60AC765A77ABB9/;s/./+0x&/g'<<<${1#?}) ));}
For your example I'd do:
read m y <<<"$#"; echo "$y-`m2n $m`"