Using and manipulating date variable inside AWK - bash

I assume that there may be a better way to do it but the only one I came up with was using AWK.
I have a file with name convention like following:
testfile_2016_03_01.txt
Using one command I am trying to shift it by one day testfile_20160229.txt
I started from using:
file=testfile_2016_03_01.txt
IFS="_"
arr=($file)
datepart=$(echo ${arr[1]}-${arr[2]}-${arr[3]} | sed 's/.txt//')
date -d "$datepart - 1 days" +%Y%m%d
the above works fine, but I really wanted to do it in AWK. The only thing I found was how to use "date" inside AWK
new_name=$(echo ${file##.*} | awk -F'_' ' {
"date '+%Y%m%d'" | getline date;
print date
}')
echo $new_name
okay so two things happen here. For some reason $4 also contains .txt even though I removed it(?) ##.*
And the main problem is I don't know how to pass the variables to that date, the below doesn't work
`awk -F'_' '{"date '-d "2016-01-01"' '+%Y%m%d'" | getline date; print date}')
ideally I want 2016-01-01 to be variables coming from the file name $2-$3-$4 and substract 1 day but I think I'm getting way too many single and double quotes here and my brain is losing..

Equivalent awk command:
file='testfile_2016_03_01.txt'
echo "${file%.*}" |
awk -F_ '{cmd="date -d \"" $2"-"$3"-"$4 " -1 days\"" " +%Y%m%d";
cmd | getline date; close(cmd); print date}'
20160229

WIth GNU awk for time functions:
$ file=testfile_2016_03_01.txt
$ awk -v file="$file" 'BEGIN{ split(file,d,/[_.]/); print strftime(d[1]"_%Y%m%d."d[5],mktime(d[2]" "d[3]" "d[4]" 12 00 00")-(24*60*60)) }'
testfile_20160229.txt

This might work for you:
file='testfile_2016_03_01.txt'
IFS='_.' read -ra a <<< "$file"
date -d "${a[1]}${a[2]}${a[3]} -1 day" "+${a[0]}_%Y%m%d.${a[4]}"

Related

unix time to date and replace in bash using awk

I am trying to convert unix time to date and time;
1436876820 blah1 stop none john
1436876820 blah0 continu none john
1436876821 blah2 stop good bob
I would like to convert the first column to have two more column date and time as below
14-07-15 13:27:00 blah1 stop none john
14-07-15 13:27:00 blah0 continu none john
14-07-15 13:27:01 blah2 stop good bob
etc..
So I have started to do the following.
IN="${1}"
for i in $(awk '{print $1}' ${IN});
do
DD=$(date -d #${i} +'%d-%m-%Y %H:%M:%S')
awk '{ ${1}="'"${DD}"'" }' < ${IN}
done
This does not work due to the syntax and give such of error:
awk: { ${1}="14-07-2015 13:27" }
awk: ^ syntax error
I could use sed instead of awk:
sed "s/^1........./${DD}/" ${IN}
Any help with awk is really welcome.
Al.
Get rid of the shell loop and just do it one awk invocation:
awk '{
cmd = "date -d #" $1 " +\"%d-%m-%Y %H:%M:%S\""
if ( (cmd | getline dd) > 0 ) {
$1 = dd
}
close(cmd)
print
}' "$1"
If you have GNU awk you can just use it's internal strftime() instead of the date+getline:
awk '{
$1 = strftime("%d-%m-%Y %H:%M:%S",$1)
print
}' "$1"

Bash string replace on command result

I have a simple bash script which is getting the load average using uptime and awk, for example
LOAD_5M=$(uptime | awk -F'load averages:' '{ print $2}' | awk '{print $2}')
However this includes a ',' at the end of the load average
e.g.
0.51,
So I have then replaced the comma with a string replace like so:
LOAD_5M=${LOAD_5M/,/}
I'm not an awk or bash wizzkid so while this gives me the result I want, I am wondering if there is a succinct way of writing this, either by:
Using awk to get the load average without the comma, or
Stripping the comma in a single line
You can do that in same awk command:
uptime | awk -F 'load averages?: *' '{split($2, a, ",? "); print a[2]}'
1.32
The 5 min load is available in /proc/loadavg. You can simply use cut:
cut -d' ' -f2 /proc/loadavg
With awk you can issue:
awk '{print $2}' /proc/loadavg
If you are not working on Linux the file /proc/loadavg will not being present. In this case I would suggest to use sed, like this:
uptime | sed 's/.*, \(.*\),.*,.*/\1/'
uptime | awk -F'load average:' '{ print $2}' | awk -F, '{print $2}'
0.38
(My uptime output has 'load average:' singular)
The load average numbers are always the last 3 fields in the 'uptime' output so:
IFS=' ,' read -a uptime_fields <<<"$(uptime)"
LOAD_5M=${uptime_fields[#]: -2:1}

format date in file using awk

Content of the file is
Feb-01-2014 one two
Mar-02-2001 three four
I'd like to format the first field (the date) to %Y%m%d format
I'm trying to use a combination of awk and date command, but somehow this is failing even though i got the feeling i'm almost there:
cat infile | awk -F"\t" '{$1=system("date -d " $1 " +%Y%m%d");print $1"\t"$2"\t"$3}' > test
this prints out date's usage pages which makes me think that the date command is triggered properly, but there is something wrong with the argument, do you see the issue somewhere?
i'm not that familiar with awk,
You don't need date for this, its simply rearranging the date string:
$ awk 'BEGIN{FS=OFS="\t"} {
split($1,t,/-/)
$1 = sprintf("%s%02d%s", t[3], (match("JanFebMarAprMayJunJulAugSepOctNovDec",t[1])+2)/3, t[2])
}1' file
20140201 one two
20010302 three four
You can use:
while read -r a _; do
date -d "$a" '+%Y%m%d'
done < file
20140201
20010302
system() returns the exit code of the command.
Instead:
cat infile | awk -F"\t" '{"date -d " $1 " +%Y%m%d" | getline d;print d"\t"$2"\t"$3}'
$ awk '{var=system("date -d "$1" +%Y%m%d | tr -d \"\\n\"");printf "%s\t%s\t%s\n", var, $2, $3}' file
201402010 one two
200103020 three four

Adding a single date to the first column of a file

I have a file that looks like
1234-00AA12 .02
5678-11BB34 .03
In a bash script I have an expression like
day=$(...)
that greps a date in the format YYYY/MM/DD (if this matters), let's say 2014/01/21 for specificity.
I want to produce the following:
2014/01/21,1,1,1234,00AA12,.02
2014/01/21,1,1,5678,11BB34,.03
(The first column is the day, the second and third columns are fixed as "1").
After a bit of googling I tried:
cat file|awk -F "-" '{split($2,array," "); printf "%s,%s,%s,%s,%s,%s\n",$day,"1","1",$1,array[1],array[2]}'> output.csv
but $day isn't working with awk.
Any help would be appreciated.
Try this awk:
awk -v d=$(date '+%Y/%m/%d') '{print d,1,1,$1,$2}' OFS=, file
2014/02/07,1,1,1234-00AA12,.02
2014/02/07,1,1,5678-11BB34,.03
$ awk -v day="$day" 'BEGIN{FS="[ -]";OFS=","} {print day,1,1,$1,$2,$3}' file
2014/01/21,1,1,1234,00AA12,.02
2014/01/21,1,1,5678,11BB34,.03
awk wouldn't understand shell variables. You need to pass those to it:
awk -vdd="$day" -F "-" '{split($2,array," "); printf "%s,%s,%s,%s,%s,%s\n",dd,"1","1",$1,array[1],array[2]}'
Moreover, rather than saying:
cat file | awk ...
avoid the useless use of cat:
awk file
With bash
day="2014/01/21"
(
IFS=,
while IFS=" -" read -ra fields; do
new=( "$day" 1 1 "${fields[#]}" )
echo "${new[*]}"
done < file
)
2014/01/21,1,1,1234,00AA12,.02
2014/01/21,1,1,5678,11BB34,.03
I run the while loop in a subshell just to keep changes to IFS localized.

Round down to nearest 5 minutes

The date command returns the current date. I want the nearest 5 minute interval. For e.g.
# date
Thu Mar 15 16:06:42 IST 2012
In this case I want to return ...
Mar 15 16:05:00
Is it possible in the shell script? or is there any one liner for this?
Update:
the date is in this format...
2012-03-10 12:59:59
Latest update:
The following command works as expected. Thanks for the response.
head r_SERVER_2012-03-10-12-55-00 | awk -F'^' '{print $7}' | awk '{split($2, a, ":"); printf "%s %s:%02d:00\n", $1, a[1],int(a[2]/5)*5}'
Correct result:
2012-03-10 12:55:00
But I want to show other fields as well other than date. The following does not work:
head r_SERVER_2012-03-10-12-55-00 | awk -F'^' '{print $1, $2, $7, $8}' | awk '{split($2, a, ":"); printf "%s %s:%02d:00\n", $1, a[1],int(a[2]/5)*5}'
Wrong result:
565 14718:00:00
It should be ...
565 123 2012-03-10 12:55:00 country
date | awk '{split($4, a, ":"); printf "%s %s %s:%02d:00", $2, $3, a[1],int(a[2]/5)*5}'
$ date="2012-03-10 12:59:59"
$ read d h m s < <(IFS=:; echo $date)
$ printf -v var "%s %s:%d:00" $d $h $(( m-(m%5) ))
$ echo "$var"
2012-03-10 12:55:00
I use process substitution in the read command to isolate changes to IFS in a subshell. `
If you have GNU AWK available, you could use this:
| gawk '{t=mktime(gensub(/[-:]/," ","g")); print strftime("%Y-%m-%d %H:%M:%S",int(t/5)*5);}'
This uses the int() function, which truncates, which sort of means "round down". If you decide you'd prefer to "round" (i.e. go to the "nearest" 5 second increment), replace int(t/5) with int((t+2.5)/5).
Of course, if you're feeling masochistic, you can do this in pure shell. This one only truncates rather than rounding up.
[ghoti#pc ~]$ fmt="%Y-%m-%d %H:%M:%S"
[ghoti#pc ~]$ date "+$fmt"
2012-03-15 07:53:37
[ghoti#pc ~]$ date "+$fmt" | while read date; do stamp="`date -jf \"$fmt\" \"$date\" '+%s'`"; date -r `dc -e "$stamp 5/ 5* p"` "+$fmt"; done
2012-03-15 07:53:35
Note that I'm using FreeBSD. If you're using Linux, then you might need to use different options for the date command (in particular, the -r and -f options I think). I'm runninB this in bash, but it should work in pure Bourne shell if that's what you need.

Resources