ISO-Format JJJJMMTT Alias/Script Linux - linux

Hay everyone,
i want to Implement a dat2iso command as a script or as an alias, which accepts a date in German format DD.MM.YYYY as a parameter and outputs the date in ISO format YYYYMMDD as output (on stdout).
Examples (as an alias or as a script in the system path;
outputs:
$ dat2iso 30.09.2021
20210930
$ dat2iso 01.01.2022
20220101
$ dat2iso 03/10/2021
Error! <-- Because the separator was. required.
$ dat2iso 03.10.21
Error! <-- Because the year YYYY was prescribed with four digits.
$ dat2iso 32.09.2021
Error! <-- Denn einen 32. gibt es nicht.
$ dat2iso 30.13.2021
Error! <-- Because there is no 13th month.
i have come sofar to change the formate but i cant figure the rest but im still working on it:
echo 30.09.2021 | awk -F '.' {'printf "%s%s%s\n",$3,$2,$1'}

A very verbose solution, but it checks for your given exemptions:
cat << EOF > dat2iso
#!/bin/bash
if [[ "\$1" == "" ]];then echo -e "NO INPUT. Exiting."; exit 1 ;fi
echo \$1 | awk -F '.' '/\//{ print "Input Error."; exit(1) }
\$1>31{ print "Too many days."; exit(1) }
\$2>12{ print "Too many months."; exit(1) }
\$3!~/[[:digit:]][[:digit:]][[:digit:]][[:digit:]]/{ print "Not enough year digits."; exit(1) }
{ print \$3\$2\$1 }'
EOF
chmod +x dat2iso
$ ./dat2iso 09.12.2021
20211209

This should achieve what you expected :
#!/bin/bash
dat2iso(){
if [[ $1 =~ ^([[:digit:]]{2})\.([[:digit:]]{2})\.([[:digit:]]{4})$ ]]; then
local date="${BASH_REMATCH[3]}${BASH_REMATCH[2]}${BASH_REMATCH[1]}"
if date -d "$date" >& /dev/null; then
echo "$date"
else
echo "Invalid date value [$date]" >&2
fi
else
echo "Invalid format of input [$1]. Correct format [DD.MM.YYYY]" >&2
fi
}
dat2iso 30.09.2021

Related

How to compare dates in Linux

My so.txt contains below line:
PRE 2022-03-16/
Date is = 2022-03-16
awsdate= cut -c 5-14 so.txt
sysdates=`date +%Y-%m-%d`
echo $sysdates
if [[ "$awsdate" == "$sysdates" ]] ;
then
echo "dates are matching"
else
echo "dates are not matching"
fi
Output:
2022-03-16
2022-03-16
dates are not matching
With above script I am trying to compare dates in Linux. Although the dates are same I am still getting dates are not matching. Why is this?
Probably a quoting error. Use quotes and command substitution properly. This works:
so.txt:
PRE 2022-03-16/
test.sh:
Note: shellcheck test.sh comes up clean now.
#!/bin/bash
awsdate="$(cut -c 5-14 so.txt)"
sysdates="$(date +%Y-%m-%d)"
echo "$sysdates"
if [[ "$awsdate" == "$sysdates" ]]; then
echo "dates are matching"
else
echo "dates are not matching"
fi
Cmd and output:
$ ./test.sh
2022-03-16
dates are matching
Run shellcheck on your original script and see what it says for errors.
Here is what I see:
Bad test.sh:
#!/bin/bash
awsdate= cut -c 5-14 so.txt
sysdates=`date +%Y-%m-%d`
echo $sysdates
if [[ "$awsdate" == "$sysdates" ]] ;
then
echo "dates are matching"
else
echo "dates are not matching"
fi
Run shellcheck on it. I am using shellcheck --version of version: 0.8.0. These are all of your errors and problems:
$ shellcheck test.sh
In test.sh line 1:
awsdate= cut -c 5-14 so.txt
^-- SC2148 (error): Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.
^-- SC1007 (warning): Remove space after = if trying to assign a value (for empty string, use var='' ... ).
In test.sh line 2:
sysdates=`date +%Y-%m-%d`
^--------------^ SC2006 (style): Use $(...) notation instead of legacy backticks `...`.
Did you mean:
sysdates=$(date +%Y-%m-%d)
In test.sh line 3:
echo $sysdates
^-------^ SC2086 (info): Double quote to prevent globbing and word splitting.
Did you mean:
echo "$sysdates"
In test.sh line 4:
if [[ "$awsdate" == "$sysdates" ]] ;
^------^ SC2154 (warning): awsdate is referenced but not assigned.
For more information:
https://www.shellcheck.net/wiki/SC2148 -- Tips depend on target shell and y...
https://www.shellcheck.net/wiki/SC1007 -- Remove space after = if trying to...
https://www.shellcheck.net/wiki/SC2154 -- awsdate is referenced but not ass...
References:
my notes on shellcheck: https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world/tree/master/bash#shellcheck

I have shell script which accepts two date parameters and needs to validate two parameters in the dd-mm-yyyy format

I have shell script which accepts two date parameters and needs to validate two parameters in the dd-mm-yyyy format.
....
DateValidate () {
date "+%d-%m-%Y" -d "$1" > /dev/null 2>&1
is_valid1=$?
date "+%d-%m-%Y" -d "$2" > /dev/null 2>&1
is_valid2=$?
if [[ "$is_valid1" != 0 || "${is_valid2}" != 0 ]];
echo "invalid dates"
else echo "passed valid arguments"
.........
Getting invalid dates but if when I do this with date "+%Y-%m-%d" then it is validating fine, please let me know any way to validate the date arguments format as ddmmyyyy.
Your primary problem is, while date will handle a wide range of formats with -d it will not take hyphen separated values as dd-mm-yyyy or mm-dd-yyyy. In a delimited format date expects mm/dd/yyyy. So you will have to reformat your date in that format before attempting conversion with date -d.
You should also validate you have the proper number of inputs as well as using local variables within your function. Adding the validations and using awk for a quick reformat of the arguments, you could do:
validdate() {
[ -z "$1" -o -z "$2" ] && { ## validate 2 arguments given
printf "error: insufficient no. of arguments.\n" >&2
return 1
}
## reformat dd-mm-yyyy as mm/dd/yyyy
local d1=$(awk -F- '{ print $2"/"$1"/"$3 }' <<< "$1")
local d2=$(awk -F- '{ print $2"/"$1"/"$3 }' <<< "$2")
## conversion to date
date -d "$d1" &>/dev/null || { printf "date1 invalid\n" >&2; return 1; }
date -d "$d2" &>/dev/null || { printf "date2 invalid\n" >&2; return 1; }
echo "dates valid"
return 0;
}
(note: if not using bash, you will need to replace the herestring <<< with a pipe.)
Adding a short script to check the input:
validdate "$1" "$2"
echo "return $?"
The function performs as you intend, e.g.
$ ./chkdates.sh 21-4-1999 23-5-1999
dates valid
return 0
Or in the case of an invalid date:
$ ./chkdates.sh 21-4-1999 32-5-1999
date2 invalid
return 1
Arguments to date -d cannot be provided in the dd-mm-yyyy format. Before using date -d, you have to convert your date parameters into a format that date -d can handle. The following works, by converting the two date parameters to the yyyymmdd format:
#! /bin/bash
validate () {
day="$(cut -d'-' -f1 <<<$1)"
month="$(cut -d'-' -f2 <<<$1)"
year="$(cut -d'-' -f3 <<<$1)"
date "+%d%m%y" -d $year$month$day > /dev/null 2>&1
}
validate $1
is_valid1=$?
validate $2
is_valid2=$?
if [[ "$is_valid1" != 0 || "${is_valid2}" != 0 ]]
then
echo "Invalid dates"
fi
Example:
./datevalid_test.sh 28-17-2020 14-04-2018
Invalid dates
To calculate the difference between two dates (in days), you can use:
#! /bin/bash
convert_to_seconds() {
day="$(cut -d'-' -f1 <<<$1)"
month="$(cut -d'-' -f2 <<<$1)"
year="$(cut -d'-' -f3 <<<$1)"
echo "$(date "+%s" -d $year$month$day)"
}
fdate="$(convert_to_seconds $1)"
sdate="$(convert_to_seconds $2)"
period=$((60*60*24))
datediff=$(((${fdate} - ${sdate})/${period}))
echo $datediff
This will calculate the difference between two dates provided in dd-mm-yyyy format, again by converting each date parameter to the yyyymmdd format. Example:
./datediff_test.sh 30-07-2020 28-07-2020
2
See https://unix.stackexchange.com/a/24636/334354 for more info on how to calculate date differences.
#tomptz, thanks for the help and below code works.
....
#! /bin/bash
convert_to_seconds() {
day="$(cut -d'-' -f1 <<<$1)"
month="$(cut -d'-' -f2 <<<$1)"
year="$(cut -d'-' -f3 <<<$1)"
echo "$(date "+%s" -d $year$month$day)"
}
fdate="$(convert_to_seconds $1)"
sdate="$(convert_to_seconds $2)"
period=$((60*60*24))
datediff=$(((${fdate} - ${sdate})/${period}))
echo $datediff
....

Check string after cat command in bash

How do I check if a string is contained in the text produced by a cat command in bash? I want to execute a certain action if the string is found in the text.
Like this, using the pipe :
cat my_file | grep my_string
if [ ! $(cat my_file.txt | grep my_text) == "" ]
then echo "exists"
else
echo "not exists"
fi;
Piping to grep is fine if it's a one-time match. Otherwise in bash you can match strings using patterns (==) or regexes (=~) like this:
# date looks like "Mon, Jun 22, 2020 11:04:54 AM"
today=$(date)
[[ $today == Mon* ]] && echo "It's the start of the week"
[[ $today =~ ^(Tue|Wed|Thu) ]] && echo "It's the middle of the week"
[[ $today == Fri* ]] && echo "Thank God It's Friday!"
This is really a comment on the other answer - you can just directly test the exit status of a command
# grep succeeds (exit status 0) if there's a match
# grep -q is to suppress the output of the matched line
# we just care whether there is a match or not
if grep -q my_text my_file.txt; then
echo "exists"
else
echo "not exists"
fi
Simply:
if grep -q my_text file; then
echo "exists"
else
echo "not exists"
fi

RHEL 7 Bash script not recognizing $args

Below is my simple function to get user inputted file name, but for some reason my input validation isn't working.
function getname
{
echo "Please enter the name of the file to install: "
read filename
if (($args > 1))
then
echo "You entered to many arguments."
echo $USAGE
exit
fi
}
getname
Bash -x test1 yields these results, as if it doesn't see any value for $args:
bash -x test1
+ getname
+ echo 'Please enter the name of the file to install: '
Please enter the name of the file to install:
+ read filename
testfile
+ (( > 1 ))
test1: line 9: ((: > 1: syntax error: operand expected (error token is "> 1")
Why isn't this working?
Thanks!
There are many ways to ignore parts after spaces (awk, cut, sed could do the work), and even warning about such thing:
#!/bin/bash
echo "Input filename:"
read input
filename=$(echo $input | awk '{ print $1 }')
echo "Filename entered is: $filename"
[ "${filename}" != "${input}" ] && echo "(warning: parts after spaces were ignored)"
Also, using read conveniently, you could directly read what you want:
read filename garbage
You could consider convert spaces to underscores (or keep spaces as part of filename like windows guys ...):
read input
filename=$(echo $input | tr ' ' '_')
BRs

In bash, how to store a return value in a variable?

I know some very basic commands in Linux and am trying to write some scripts. I have written a function which evaluates the sum of last 2-digits in a 5-digit number. The function should concatenate this resultant sum in between the last 2-digits and return it. The reason I want to return this value is because I will be using this value in the other function.
Ex: if I have 12345, then my function will calculate 4+5 and return 495.
#!/bin/bash
set -x
echo "enter: "
read input
function password_formula
{
length=${#input}
last_two=${input:length-2:length}
first=`echo $last_two| sed -e 's/\(.\)/\1 /g'|awk '{print $2}'`
second=`echo $last_two| sed -e 's/\(.\)/\1 /g'|awk '{print $1}'`
let sum=$first+$second
sum_len=${#sum}
echo $second
echo $sum
if [ $sum -gt 9 ]
then
sum=${sum:1}
fi
value=$second$sum$first
return $value
}
result=$(password_formula)
echo $result
I am trying to echo and see the result but I am getting the output as shown below.
-bash-3.2$ ./file2.sh
+++ password_formula
+++ echo 'enter: '
+++ read input
12385
+++ length=8
+++ last_two=85
++++ echo 85
++++ sed -e 's/\(.\)/\1 /g'
++++ awk '{print $2}'
+++ first=5
++++ echo 85
++++ sed -e 's/\(.\)/\1 /g'
++++ awk '{print $1}'
+++ second=8
+++ let sum=5+8
+++ sum_len=2
+++ echo 5
+++ echo 8
+++ echo 13
+++ '[' 13 -gt 9 ']'
+++ sum=3
+++ value=835
+++ return 835
++ result='enter:
5
8
13'
++ echo enter: 5 8 13
enter: 5 8 13
I also tried to print the result as:
password_formula
RESULT=$?
echo $RESULT
But that is giving some unknown value:
++ RESULT=67
++ echo 67
67
How can I properly store the correct value and print (to double check) on the screen?
Simplest answer:
the return code from a function can be only a value in the range from 0 to 255 .
To store this value in a variable you have to do like in this example:
#!/bin/bash
function returnfunction {
# example value between 0-255 to be returned
return 23
}
# note that the value has to be stored immediately after the function call :
returnfunction
myreturnvalue=$?
echo "myreturnvalue is "$myreturnvalue
The return value (aka exit code) is a value in the range 0 to 255 inclusive. It's used to indicate success or failure, not to return information. Any value outside this range will be wrapped.
To return information, like your number, use
echo "$value"
To print additional information that you don't want captured, use
echo "my irrelevant info" >&2
Finally, to capture it, use what you did:
result=$(password_formula)
In other words:
echo "enter: "
read input
password_formula()
{
length=${#input}
last_two=${input:length-2:length}
first=`echo $last_two| sed -e 's/\(.\)/\1 /g'|awk '{print $2}'`
second=`echo $last_two| sed -e 's/\(.\)/\1 /g'|awk '{print $1}'`
let sum=$first+$second
sum_len=${#sum}
echo $second >&2
echo $sum >&2
if [ $sum -gt 9 ]
then
sum=${sum:1}
fi
value=$second$sum$first
echo $value
}
result=$(password_formula)
echo "The value is $result"
It is easy you need to echo the value you need to return and then capture it like below
demofunc(){
local variable="hellow"
echo $variable
}
val=$(demofunc)
echo $val
Use the special bash variable "$?" like so:
function_output=$(my_function)
function_return_value=$?
The answer above suggests changing the function to echo data rather than return it so that it can be captured.
For a function or program that you can't modify where the return value needs to be saved to a variable (like test/[, which returns a 0/1 success value), echo $? within the command substitution:
# Test if we're remote.
isRemote="$(test -z "$REMOTE_ADDR"; echo $?)"
# Or:
isRemote="$([ -z "$REMOTE_ADDR" ]; echo $?)"
# Additionally you may want to reverse the 0 (success) / 1 (error) values
# for your own sanity, using arithmetic expansion:
remoteAddrIsEmpty="$([ -z "$REMOTE_ADDR" ]; echo $((1-$?)))"
E.g.
$ echo $REMOTE_ADDR
$ test -z "$REMOTE_ADDR"; echo $?
0
$ REMOTE_ADDR=127.0.0.1
$ test -z "$REMOTE_ADDR"; echo $?
1
$ retval="$(test -z "$REMOTE_ADDR"; echo $?)"; echo $retval
1
$ unset REMOTE_ADDR
$ retval="$(test -z "$REMOTE_ADDR"; echo $?)"; echo $retval
0
For a program which prints data but also has a return value to be saved, the return value would be captured separately from the output:
# Two different files, 1 and 2.
$ cat 1
1
$ cat 2
2
$ diffs="$(cmp 1 2)"
$ haveDiffs=$?
$ echo "Have differences? [$haveDiffs] Diffs: [$diffs]"
Have differences? [1] Diffs: [1 2 differ: char 1, line 1]
$ diffs="$(cmp 1 1)"
$ haveDiffs=$?
$ echo "Have differences? [$haveDiffs] Diffs: [$diffs]"
Have differences? [0] Diffs: []
# Or again, if you just want a success variable, reverse with arithmetic expansion:
$ cmp -s 1 2; filesAreIdentical=$((1-$?))
$ echo $filesAreIdentical
0
It's due to the echo statements. You could switch your echos to prints and return with an echo. Below works
#!/bin/bash
set -x
echo "enter: "
read input
function password_formula
{
length=${#input}
last_two=${input:length-2:length}
first=`echo $last_two| sed -e 's/\(.\)/\1 /g'|awk '{print $2}'`
second=`echo $last_two| sed -e 's/\(.\)/\1 /g'|awk '{print $1}'`
let sum=$first+$second
sum_len=${#sum}
print $second
print $sum
if [ $sum -gt 9 ]
then
sum=${sum:1}
fi
value=$second$sum$first
echo $value
}
result=$(password_formula)
echo $result
Something like this could be used, and still maintaining meanings of return (to return control signals) and echo (to return information) and logging statements (to print debug/info messages).
v_verbose=1
v_verbose_f="" # verbose file name
FLAG_BGPID=""
e_verbose() {
if [[ $v_verbose -ge 0 ]]; then
v_verbose_f=$(tempfile)
tail -f $v_verbose_f &
FLAG_BGPID="$!"
fi
}
d_verbose() {
if [[ x"$FLAG_BGPID" != "x" ]]; then
kill $FLAG_BGPID > /dev/null
FLAG_BGPID=""
rm -f $v_verbose_f > /dev/null
fi
}
init() {
e_verbose
trap cleanup SIGINT SIGQUIT SIGKILL SIGSTOP SIGTERM SIGHUP SIGTSTP
}
cleanup() {
d_verbose
}
init
fun1() {
echo "got $1" >> $v_verbose_f
echo "got $2" >> $v_verbose_f
echo "$(( $1 + $2 ))"
return 0
}
a=$(fun1 10 20)
if [[ $? -eq 0 ]]; then
echo ">>sum: $a"
else
echo "error: $?"
fi
cleanup
In here, I'm redirecting debug messages to separate file, that is watched by tail, and if there is any changes then printing the change, trap is used to make sure that background process always ends.
This behavior can also be achieved using redirection to /dev/stderr, But difference can be seen at the time of piping output of one command to input of other command.
Ok the main answers to this are problematic if we have errexit set, e.g.
#!/bin/bash
set -o errexit
my_fun() {
# returns 0 if the first arguments is "a"
# 1 otherwise
[ "${1}" = "a" ]
}
my_fun "a"
echo "a=$?"
my_fun "b"
echo "b=$?"
In this case bash just exit when the result is not 0, e.g. this only prints the a line.
./test_output.sh
a=0
As already said well here probably the most correct answer is something like this:
# like this
my_val=0 ; my_fun "a" || my_val=$?
echo "a=${my_val}"
# or this
my_fun "b" && my_val=0 || my_val=$?
echo "b=${my_val}"
This print all the values correctly without error
a=0
b=1
I don't know if the "echo" implementation is the most correct, as I still is not clear to me if
$() creates a subshell or not.
I have a function for example that opens up a
file descriptor in a function and return the number and it seems that bash closes the fd after exiting the function.
(if someone can help me here :-)

Resources