Calculating and displaying date difference - unix

I have two dates which im loading into variables using
a=`date +%s`
b=`date +%s`
i want to know the difference between times e.g difference 00:00:10 and so on , i do calculate it using
diff=$(( b-a ))
echo "$(( diff/3600 )):$((( diff/60)%60)):$((diff%60))"
but the output is 0:0:07 , how can i convert it on 2points = on 00:00:07?

If the string length is 1 then added the zero with value
hour=$(( diff/3600 ))
min=$((( diff/60)%60))
sec=$((diff%60))
[[ ${#hour} == 1 ]] && hour="0$hour" || hour="$hour"
[[ ${#min} == 1 ]] && min="0$min" || min="$min"
[[ ${#sec} == 1 ]] && sec="0$sec" || sec="$sec"
echo "$hour:$min:$sec"
Output:
00:00:16

Related

Not In like database in AWK

How to simplify the != statements. I have plenty of values like this
if (charNr%2 == 0 && newChar != " " && newChar !="0" && newChar !="1" && newChar !="2" && newChar !="3" && newChar !="4" && newChar !="5" && newChar !="6" && newChar !="7" && newChar !="8" && newChar !="9" ) {newStr = newStr newChar }
I want to use it in AWK AIX 7.1.2 fuctions. Please help me.
I am expecting something like
if (charNr%2 == 0 && newChar NOT IN (1 2 3 4 5 6 7 8 9 0) ) {newStr = newStr newChar }
The in operator in Awk works with array keys, so you can do:
keys["foo"];
"foo" in keys # true
For your example, you would have to create an array containing all the keys first:
keys[1]; keys[2]; keys[3]; # etc.
In this specific case you could use a loop to help you:
for (i = 0; i < 10; ++i) keys[i] # set keys from 0 to 9
newChar in keys # true if newChar is 0-9
In a general case, you can use:
input = "first,second,third,fourth"
n = split(input, temp, /,/)
for (i = 1; i <= n; ++i) keys[temp[i]]
if your black list is single digits there are easier ways, but assuming that you have a list of arbitrary tokens, you can use this trick
awk -v n='t1 t2 t3 t4' 'FS n FS !~ FS $1 FS'
it adds the FS to the beginning and end of the list and check for pattern match with a FS padded keyword (here $1, replace with your variable). Here assuming the default field delimiter is used, otherwise use the same delimiter in the list of tokens.
For example,
$ awk -v n='11 13 17 19' 'FS n FS !~ FS $1 FS' < <(seq 10 20)
10
12
14
15
16
18
20
if your list is arbitrary single chars (or digits), you can simplify it to
$ awk 'FS $1 FS !~ / [2357] /' < <(seq 10)
1
4
6
8
9
10
To simplify the != is to have the unwanted values in an array and to (value in array==0), like this:
$ awk '
BEGIN {
for(i=0;i<=9;i++) a[i] # add values 0...9 to array a
}
($1 in a==0) { # if value is not found in array
print # print it
}' <(seq 1 12) # test values
10
11
12

How to use AND in R to modify dataframe

I have a data matrix 1200 (row, sample name)* 20000 (col, gene name), I want to delete row when my interested 5 genes have zero values in all samples
command I used for single gene:
allexp <-preallexp[preallexp$GZMB > 0, ]
but I want to use AND in above command, like this:
allexp <-preallexp[preallexp$GZMB && preallexp$TP53 && preallexp$EGFR && preallexp$BRAF && preallexp$VGEF > 0, ]
but this command doesnt work, please I need your help..How to use AND in above command.
EDIT: in response to OP.
I'm sure there's a much more efficient way to code this, but this is what you're after:
allexp <-preallexp[preallexp$GZMB + preallexp$TP53 + preallexp$EGFR +
preallexp$BRAF + preallexp$VGEF > 0, ]
Unless you have negative expression values I would have thought mkt's should work. But here is mine. It will remove values rows where each of the 5 genes and a value of 0
which(preallexp$GZMB == 0 && preallexp$TP53 &&
preallexp$EGFR == 0 && preallexp$BRAF == 0 && preallexp$VGEF == 0)
This gives so the rows where all 5 genes have a value of zero
So we can remove these rows if from the dataframe like follows
allexp <-preallexp[
-(which(preallexp$GZMB == 0 && preallexp$TP53 &&
preallexp$EGFR == 0 && preallexp$BRAF == 0 && preallexp$VGEF == 0)), ]

How to check if the variable value in AWK script is null or empty?

I am using AWK script to process some logs.
At one place I need to check if the variable value is null or empty to make some decision.
Any Idea how to achieve the same?
awk '
{
{
split($i, keyVal, "#")
key=keyVal[1];
val=keyVal[2];
if(val ~ /^ *$/)
val="Y";
}
}
' File
I have tried with
1) if(val == "")
2) if(val ~ /^ *$/)
not working in both cases.
The comparison with "" should have worked, so that's a bit odd
As one more alternative, you could use the length() function, if zero, your variable is null/empty. E.g.,
if (length(val) == 0)
Also, perhaps the built-in variable NF (number of fields) could come in handy? Since we don't have access to your input data it's hard to say though, but another possibility.
You can directly use the variable without comparison, an empty/null/zero value is considered false, everything else is true.
See here :
# setting default tag if not provided
if (! tag) {
tag="default-tag"
}
So this script will have the variable tag with the value default-tag except if the user call it like this :
$ awk -v tag=custom-tag -f script.awk targetFile
This is true as of :
GNU Awk 4.1.3, API: 1.1 (GNU MPFR 3.1.4, GNU MP 6.1.0)
It works just fine for me
$ awk 'BEGIN{if(val==""){print "null or empty"}}'
null or empty
You can't differentiate between variable being empty and null, when you access "unset" variable, awk just initializes it with default value(here it is "" - empty string). You can use some sort of workaround, for example, setting val_accessed variable to 0 and then to 1 when you access it. Or more simple approach(somewhat "hackish") setting val to "unitialized"(or to some other value which can't appear when running your program).
PS: your script looks strange for me, what are the nested brackets for?
I accidentally discovered this less-used function specific in gawk that could help differentiate :
****** gawk-only ******
BEGIN {
$0 = "abc"
print NF, $0
test_function()
test_function($(NF + 1))
test_function("")
test_function($0)
}
function test_function(_) { print typeof(_) }
1 abc
untyped
unassigned
string
string
So it seems, for non-numeric-like data :
absolutely no input to function at all : untyped
non-existent or empty field, including $0 : unassigned
any non-numeric-appearing string, including "" : string
Here's the chaotic part - numeric data :
strangely enough, for absolutely identical input, only differing between using $0 vs. $1 in function call, you frequently get a different value for typeof()
even a combination of both leading and trailing spaces doesn't prevent gawk from identifying it as strnum
[123]:NF:1
$0 = number:123 $1 = strnum:123 +$1 = number:123
[ 456.33]:NF:1
$0 = string: 456.33 $1 = strnum:456.33 +$1 = number:456.33000
[ 19683 ]:NF:1
$0 = string: 19683 $1 = strnum:19683 +$1 = number:19683
[-20.08554]:NF:1
$0 = number:-20.08554 $1 = strnum:-20.08554 +$1 = number:-20.08554
+/- inf/nan (same for all 4):
[-nan]:NF:1
$0 = string:-nan $1 = strnum:-nan +$1 = number:-nan
this one is a string because it was made from sprintf() :
[0x10FFFF]:NF:1
$0 = string:0x10FFFF $1 = string:0x10FFFF +$1 = number:0
using -n / --non-decimal-data flag, all stays same except
[0x10FFFF]:NF:1
$0 = string:0x10FFFF $1 = strnum:0x10FFFF +$1 = number:1114111
Long story short, if you want your gawk function to be able to differentiate between
empty-string input (""), versus
actually no input at all
e.g. when original intention is to directly apply changes to $0
then typeof(x) == "untyped" seems to be the most reliable indicator.
It gets worse when null-string padding versus a non-empty string of all zeros ::
function __(_) { return (!_) ":" (!+_) }
function ___(_) { return (_ == "") }
function ____(_) { return (!_) ":" (!""_) }
$0--->[ "000" ] | __(""$0)-->{ !(""$0) : !+(""$0) }-->[ 0:1 ]
___($0)-->{ $0=="" }-->[ 0 ] | ____($0)-->{ ! $0 : (!""$0) }-->[ 1:1000 ]
$0--->[ "" ] | __(""$0)-->{ !(""$0) : !+(""$0) }-->[ 1:1 ]
___($0)-->{ $0=="" }-->[ 1 ] | ____($0)-->{ ! $0 : (!""$0) }-->[ 1:1 ]
$0--->[ " -0.0 -0" ] | __(""$0)-->{ !(""$0) : !+(""$0) }-->[ 0:1 ]
___($0)-->{ $0=="" }-->[ 0 ] | ____($0)-->{ ! $0 : (!""$0) }-->[ 0:1 -0.0 -0 ]
$0--->[ " 0x5" ] | __(""$0)-->{ !(""$0) : !+(""$0) }-->[ 0:1 ]
___($0)-->{ $0=="" }-->[ 0 ] | ____($0)-->{ ! $0 : (!""$0) }-->[ 0:1 0x5 ]

how to find the difference between two datetimes (weekend days are excluded) in Unix?

The same problem to Find day difference between two dates (excluding weekend days) but it is for javascript. How to do that in Unix (KSH)?
Here is my Bash script, I think in Ksh it will be similar.
#! /bin/bash
#Usage dateDiff startDate endDate
startDate="$1 00:00:00"
endDate="$2 tomorrow 00:00:00" #tomorrow to include both days
stampEnd=`date -d "$endDate" +%s`
stampStart=`date -d "$startDate" +%s`
#difference in calendar days
daysDiff=`echo "($stampEnd - $stampStart) / (60 * 60 * 24)" | bc`;
#week day
weekDay=`date -d "$endDate" +%u`;
weekEndsLastWeek=`echo "$weekDay - 6" | bc`;
if test $weekEndsLastWeek -lt 0; then
weekEndsLastWeek=0;
fi
if test $weekEndsLastWeek -gt $daysDiff; then
weekEndsLastWeek=$daysDiff
fi
#normalize - make endDate a Sunday
if test $weekDay -ne 1; then #if not a Sunday already
daysDiffSunday=`echo "$daysDiff - ($weekDay - 1)" | bc`;
else
daysDiffSunday=$daysDiff;
fi
firstWeekends=0;
weekends=0;
if test $daysDiffSunday -ge 0; then
firstWeekends=`echo "$daysDiffSunday % 7" | bc`;
if test $firstWeekends -gt 2; then
firstWeekends=2
fi
weekends=`echo "$daysDiffSunday / 7 * 2" | bc`;
fi;
echo "$daysDiff - $weekends - $firstWeekends - $weekEndsLastWeek" | bc
My test data:
04/20/2012 04/22/2012 1
04/20/2012 04/25/2012 4
04/20/2012 04/30/2012 7
04/20/2012 04/28/2012 6
04/18/2012 04/21/2012 3
04/18/2012 04/22/2012 3
04/14/2012 04/21/2012 5
04/14/2012 04/22/2012 5
04/15/2012 04/21/2012 5
04/15/2012 04/22/2012 5
Test script:
allPassed=1
while read line; do
set $line;
result=`./dateDiff $1 $2`;
expected="$3";
if test "$result" -ne "$expected"; then
echo "Error in test $line: expected $expected, result $result" 1>&2
allPassed=0
fi;
done
if test $allPassed -eq 1; then
echo "All tests passed";
fi
#/bin/ksh
DATE1="2005-09-01"
DATE2="2011-02-20"
typeset -L4 y1 y2
typeset -Z -R2 m d
y1=$DATE1
y2=$DATE2
c=0
while [[ $y1 -le $y2 ]]
do
for m in 1 2 3 4 5 6 7 8 9 10 11 12
do
for d in $(cal $m $y1)
do
[[ "${d# }" < "A" ]] && {
(( c = c + 1 ))
[[ "$y1-$m-$d" = "$DATE1" ]] && d1=$c
[[ "$y1-$m-$d" = "$DATE2" ]] && {
d2=$c
break;
}
}
done
done
(( y1 = y1 + 1 ))
done
(( days = d2 - d1 ))
echo $days

How to compare versions of some products in unix ksh shell?

Format of versions - X.X.X.X.
Where X - number.
What is the best way to compare two versions?
I use following code:
compareVersions()
{
VER_1=$1
VER_2=$2
print -R "$VER_1"| IFS=. read v1_1 v1_2 v1_3 v1_4
print -R "$VER_2"| IFS=. read v2_1 v2_2 v2_3 v2_4
RESULT="0"
if [[ "${v1_1}" -lt "${v2_1}" ]]
then
RESULT="-1"
elif [[ "${v1_1}" -gt "${v2_1}" ]]
then
RESULT="1"
elif [[ "${v1_2}" -lt "${v2_2}" ]]
then
RESULT="-1"
elif [[ "${v1_2}" -gt "${v2_2}" ]]
then
RESULT="1"
elif [[ "${v1_3}" -lt "${v2_3}" ]]
then
RESULT="-1"
elif [[ "${v1_3}" -gt "${v2_3}" ]]
then
RESULT="1"
elif [[ "${v1_4}" -lt "${v2_4}" ]]
then
RESULT="-1"
elif [[ "${v1_4}" -gt "${v2_4}" ]]
then
RESULT="1"
fi
echo "$RESULT"
}
But I do not like it - it is very straightforward.
Maybe is there much correct way to compare versions?
Pure Bash / Ksh:
compareVersions ()
{
typeset IFS='.'
typeset -a v1=( $1 )
typeset -a v2=( $2 )
typeset n diff
for (( n=0; n<4; n+=1 )); do
diff=$((v1[n]-v2[n]))
if [ $diff -ne 0 ] ; then
[ $diff -le 0 ] && echo '-1' || echo '1'
return
fi
done
echo '0'
} # ---------- end of function compareVersions ----------
Maybe you could use awk?
echo $VER_1 $VER2 | \
awk '{ split($1, a, ".");
split($2, b, ".");
for (i = 1; i <= 4; i++)
if (a[i] < b[i]) {
x =-1;
break;
} else if (a[i] > b[i]) {
x = 1;
break;
}
print x;
}'
There isn't a perfect way to do this. As shown you could use array / loop for the numbers, also in bash.
You can use sort -V to sort the lines with versions and match your version against the output:
% cat sorttest
#!/bin/sh
version_lt() {
echo "$1\n$2" | sort -V | head -n 1 | grep -q "$1"
}
display_versioncmp() {
version_lt "$1" "$2" && echo "$1 < $2" || echo "$1 > $2"
}
X="1.2.3"
Y="11.2.3"
Z="1.22.3"
display_versioncmp "$X" "$Y"
display_versioncmp "$Y" "$X"
display_versioncmp "$X" "$Z"
display_versioncmp "$Z" "$X"
display_versioncmp "$Z" "$Y"
display_versioncmp "$Y" "$Z"
% ./sorttest
1.2.3 < 11.2.3
11.2.3 > 1.2.3
1.2.3 < 1.22.3
1.22.3 > 1.2.3
1.22.3 < 11.2.3
11.2.3 > 1.22.3
If you can cheat by using Perl in your shell script, try it's built-in handling of version strings with string comparison operators:
V1=1.1.3; V2=1.1
echo $(perl -e '($x,$y)=#ARGV; print $x cmp $y' $V1 $V2)
You could also do away with the Perl variables and just use shift:
result=$(perl -e 'print shift cmp shift' $V1 $V2)
But that fails on versions > 10. So you could try this instead:
perl -e '($a,$b)=#ARGV; for ($a,$b) {s/(\d+)/sprintf "%5d", $1/ge}; print $a cmp $b;' 12.1.3 9.0.2
The sprintf of "%5d" is to make sure it will even work for Firefox, until version 99999... :-)
Obviously, you could also use the other Perl string operators like gt, lt, ge and le.
I had this problem, and after solving it looked to see if there was a better answer already available. My version allows for comparing different length version strings, and is the version_ge() function below, which should be used as a "greater or equal to" operator, as in
if version_ge "$version" "1.2.3.4"; then ...
#!/bin/sh
# Usage: split "<word list>" <variable1> <variable2>...
# Split a string of $IFS seperated words into individual words, and
# assign them to a list of variables. If there are more words than
# variables then all the remaining words are put in the last variable;
# use a dummy last variable to collect any unwanted words.
# Any variables for which there are no words are cleared.
# eg. split 'hello Fred this is Bill' greeting who extra
# sets greeting=hello who=Fred extra="this is Bill"
# and split "$list" word list # "pops" the first word from a list
split()
{
# Prefix local names with the function name to try to avoid conflicts
# local split_wordlist
split_wordlist="$1"
shift
read "$#" <<EOF-split-end-of-arguments
${split_wordlist}
EOF-split-end-of-arguments
}
# Usage: version_ge v1 v2
# Where v1 and v2 are multi-part version numbers such as 12.5.67
# Missing .<number>s on the end of a version are treated as .0, & leading
# zeros are not significant, so 1.2 == 1.2.0 == 1.2.0.0 == 01.2 == 1.02
# Returns true if v1 >= v2, false if v1 < v2
version_ge()
{
# Prefix local names with the function name to try to avoid conflicts
# local version_ge_1 version_ge_2 version_ge_a version_ge_b
# local version_ge_save_ifs
version_ge_v1="$1"
version_ge_v2="$2"
version_ge_save_ifs="$IFS"
while test -n "${version_ge_v1}${version_ge_v2}"; do
IFS="."
split "$version_ge_v1" version_ge_a version_ge_v1
split "$version_ge_v2" version_ge_b version_ge_v2
IFS="$version_ge_save_ifs"
#echo " compare $version_ge_a $version_ge_b"
test "0$version_ge_a" -gt "0$version_ge_b" && return 0 # v1>v2: true
test "0$version_ge_a" -lt "0$version_ge_b" && return 1 # v1<v2:false
done
# version strings are both empty & no differences found - must be equal.
return 0 # v1==v2: true
}
Here is a slightly improved method previously posted by schot. This method can save your life when there is no bash, sort -V commands etc. (for instance, in some docker images).
compareVersions() {
echo $1 $2 | \
awk '{ split($1, a, ".");
split($2, b, ".");
res = -1;
for (i = 1; i <= 3; i++){
if (a[i] < b[i]) {
res =-1;
break;
} else if (a[i] > b[i]) {
res = 1;
break;
} else if (a[i] == b[i]) {
if (i == 3) {
res = 0;
break;
} else {
continue;
}
}
}
print res;
}'
}

Resources