How can I change command and option about 'date' command in bash? - bash

I want to convert this bash command to shell script.
BASH
Input:
date --date="Wed Aug 25 22:37:44 +0900 2021" +"%s"
Output:
1629898664
SHELL
tmp.sh:
function time(a, b, c, d, e) { return date --date="a b c d +0900 e" +"%s" }
{print time($1, $2, $3, $4, $5}
timeline:
Wed Aug 25 22:37:44 2021
Command:
awk -f tmp.sh timeline
Output:
awk: tmp.sh:1: function cvtTime(w) { return date --date="Thu May 14 23:40:52 +0900 2020" +"%s" }
awk: tmp.sh:1: ^ syntax error
What about timeline file has multiple lines? Like:
Wed Aug 25 22:37:44 2021 JACK
Wed Aug 26 22:37:44 2021 EMILY
Wed Aug 27 22:37:44 2021 SAM
I tried:
#!/bin/bash
while read -r line; do
date --date="${1} ${2} ${3} ${4} +0900 ${5}" +"%s"
done
Want:
1629898664 JACK
1629985064 EMILY
1630071464 SAM
But it doesn't work :(

It seems that you want a shell script that is invoked with five command line parameters:
A weekday (in a three-letter format)
A month (in a three-letter format)
Day-of-month
A time expression (HH:MM:SS)
A year (four digits)
(Note that 1. is redundant, it is implied by 2., 3., and 5.)
Hence a somewhat minimal shell script would look sth. like this:
#!/bin/bash
date --date="${1} ${2} ${3} ${4} +0900 ${5}" +"%s"
Of course, this can be greatly improved, e.g., by adding sanity checks for the passed parameters.
In case you want to store the date information in a file so that you can pass a single filename parameter to the script instead (allowing for multiple such lines), the following variation will do:
#!/bin/bash
while read -a i; do
echo $(date --date="${i[0]} ${i[1]} ${i[2]} ${i[3]} +0900 ${i[4]}" +"%s") ${i[5]}
done < ${1}
Note, however, that this version expects an additional name parameter after the date information in each line.
In any event, no need for awk here.

Related

Bash - optionally set env var in command substitution

The following works without issue:
timestamp=$(TZ=America/New_York date)
echo $timestamp
which (if saved in a file called /tmp/foo) results in:
$ /tmp/foo
Thu Dec 23 21:03:41 EST 2021
This code also works:
timezone=$1
timestamp=$(TZ=$timezone date)
echo "$timestamp"
well...sort of; when run with an argument, it does what I want:
$ date
Thu Dec 23 21:05:03 EST 2021
$ /tmp/foo Asia/Calcutta
Fri Dec 24 07:35:11 IST 2021
but when run without an argument, it reverts to UTC (because TZ becomes set to an empty value, which executes different code than if TZ is just not set at all):
$ /tmp/foo Asia/Calcutta
Fri Dec 24 07:37:16 IST 2021
$ /tmp/foo
Fri Dec 24 02:07:19 UTC 2021
So I should only set the TZ variable in the subshell if it's provided, right? This does not work:
timezone=$1
if [[ -n "$timezone" ]]; then
tzstring="TZ=$timezone"
fi
timestamp=$($tzstring date)
echo "$timestamp"
Without an argument, it's fine:
$ /tmp/foo
Thu Dec 23 21:09:07 EST 2021
but with an argument, it fails:
$ /tmp/foo Asia/Calcutta
/tmp/foo: line 12: TZ=Asia/Calcutta: No such file or directory
It's trying to execute that first element.
I can get it to work exactly as I'd like to with this code:
if [[ -n "$timezone" ]]; then
timestamp=$(TZ=$timezone date)
else
timestamp=$(date)
fi
which results in:
$ /tmp/foo
Thu Dec 23 21:13:19 EST 2021
$ /tmp/foo Asia/Calcutta
Fri Dec 24 07:43:21 IST 2021
but surely there's a better way to do this that reduces the code duplication. I'd rather not use a function, but if I didn't have that stipulation, I could maybe make TZ local. The only way I can think of to do all of what I want involves using eval, which I'm not really willing to do (and I'm not really sure I know how to do it even then).
Set the timezone only when there's a parameter. You can do that by exporting $TZ inside the subshell whose output you're capturing.
timestamp=$(if [ "$1" ] ; then export TZ=$1 ; fi; date)
echo $timestamp

Preappend timestamp in shell script which has embedded redirection

I have a unix shell script like below. I wanted to preappend a timestamp in front of every line of out.log. The general solution was create another script preappend.sh and execute the script like this:
(./a.sh 2>&1 ) | ./b.sh > out.log
However the original shell script has a line exec 2>out.log (I have commented this out below for my testing earlier). In real life this line is not commented. Could someone teach me how I would preappend the timestamp in out.log when there is a exec 2> in place?
benny
------ my script a.sh ---------
#!/bin/sh
#exec 2>out.log
set -x
echo 'hello world'
sleep 2
echo 'you rocks'
------end---------
---- preappend.sh ---
#!/bin/bash
while read line ; do
echo "$(date '+%Y%m%d %H:%M:%S'): ${line}"
done
-------end------------
Does this address the problem:
origScript.sh 2>&1 | awk '{ printf strftime() " " $0 "\n" }'
We can do a small test to check if this is working -
while [ 1 ]
do
date
(>&2 echo "error")
sleep 1
done 2>&1 | awk '{ printf strftime() " " $0 "\n" }'
It returns something like this:
Tue Sep 20 19:11:43 UTC 2016 Tue Sep 20 19:11:43 UTC 2016
Tue Sep 20 19:11:43 UTC 2016 error
Tue Sep 20 19:11:44 UTC 2016 Tue Sep 20 19:11:44 UTC 2016
Tue Sep 20 19:11:44 UTC 2016 error
...

I want to convert 18-Aug-2015 date format to '2015-08-18' using shell script

I want to convert 18-Aug-2015 date format to '2015-08-18' using shell script
Try this formatting:
$ date +"%Y-%m-%d"
http://www.cyberciti.biz/faq/linux-unix-formatting-dates-for-display/
The -d option is GNU specific.
Here, you don't need to do date calculation, just rewrite the string which already contains all the information:
a=$(printf '%s\n' "$Prev_date" | awk '{
printf "%04d-%02d-%02d\n", $6, \
(index("JanFebMarAprMayJunJulAugSepOctNovDec",$2)+2)/3,$3}')
Without awk, assuming your initial date is in $mydate:
IFS=- d=($mydate)
months=(Zer Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
z=1
while [[ ${months[$z]} != ${d[1]} ]]; do z=$((z+1)); done
printf "%s-%02d-%s\n" ${d[2]} $z ${d[0]}

Bash script assistance with renaming file using existing parts of filename

I'm looking for help with a bash script to do some renaming of files for me. I don't know much about bash scripting, and what I have read is overwhelming. It's a lot to know/understand for the limited applications I will probably have.
In Dropbox, my media files are named something like:
Photo Jul 04, 5 49 44 PM.jpg
Video Jun 22, 11 21 00 AM.mov
I'd like them to be renamed in the following format: 2015-07-04 1749.ext
Some difficulties:
The script has to determine if AM or PM to put in the correct 24-hour format
The year is not specified; it is safe to assume the current year
The date, minute and second have a leading zero, but the hour does not; therefore the position after the hour is not absolute
Any assistance would be appreciated. FWIW, I'm running MacOS.
Mac OSX
This uses awk to reformat the date string:
for f in *.*
do
new=$(echo "$f" | awk -F'[ .]' '
BEGIN {
split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec",month)
for (i in month) {
nums[month[i]]=i
}
}
$(NF-1)=="PM" {$4+=12;}
{printf "%s 2015-%02i-%02i %02i%02i.%s",$1,nums[$2],$3,$4,$5,$8;}
')
mv "$f" "$new"
done
After the above was run, the files are now named:
$ ls -1 *.*
Photo 2015-07-04 1749.jpg
Video 2015-06-22 1121.mov
The above was tested on GNU awk but I don't believe that I have used any GNU-specific features.
GNU/Linux
GNU date has a handy feature for interpreting human-style date strings:
for f in *.*
do
prefix=${f%% *}
ext=${f##*.}
datestr=$(date -d "$(echo "$f" | sed 's/[^ ]* //; s/[.].*//; s/ /:/3; s/ /:/3; s/,//')" '+%F %H%M')
mv "$f" "$prefix $datestr.$ext"
done
Here is an example of the script in operation:
$ ls -1 *.*
Photo Jul 04, 5 49 44 PM.jpg
Video Jun 22, 11 21 00 AM.mov
$ bash script
$ ls -1 *.*
Photo 2015-07-04 1749.jpg
Video 2015-06-22 1121.mov
While not a simple parse and reformat for date, it isn't that difficult. The bash string tools of parameter expansion/substring removal are all you need to parse the pieces of the date into a format that date can use to output a new date string in the format for use in a filename. (see String Manipulation ) date -d is used to generate a new date string based on the contents of the original filename.
Note: the following presumes the dropbox filenames are in the format you have specified. (it doesn't care what the first part of the name or extension is as long as it matches the format you have specified) Here is an example of properly isolating the pieces of the filename needed to generate a date in the format specified)
Further, all spaces have been removed from the filename. While you originally showed a space between the day and hours, I will not provide an example of poor practice by inserting a space in a filename. As such, the spaces have been replaced with '_' and '-':
#!/bin/bash
# Photo Jul 04, 5 49 44 PM.jpg
# Video Jun 22, 11 21 00 AM.mov
# fn="Photo Jul 04, 5 49 44 PM.jpg"
fn="Video Jun 22, 11 21 00 AM.mov"
ext=${fn##*.} # determine extension
prefix=${fn%% *} # determine prefix (Photo or Video)
datestr=${fn%.${ext}} # remove extension from filename
datestr=${datestr#${prefix} } # remove prefix from datestr
day=${datestr%%,*} # isolate Month and date in day
ampm=${datestr##* } # isloate AM/PM in ampm
datestr=${datestr% ${ampm}} # remove ampm from datestr
timestr=${datestr##*, } # isolate time in timestr
timestr=$(tr ' ' ':' <<<"$timestr") # translate spaces to ':' using herestring
cmb="$day $timestr $hr" # create combined date/proper format
## create date/time string for filename
datetm=$(date -d "$cmb" '+%Y%m%d-%H%M')
newfn="${prefix}_${datetm}.${ext}"
## example moving of file to new name
# (assumes you handle the path correctly)
printf "mv '%s' %s\n" "$fn" "$newfn"
# mv "$fn" "$newfn" # uncomemnt to actually use
exit 0
Example/Output
$ bash dateinfname.sh
mv 'Video Jun 22, 11 21 00 AM.mov' Video_20150622-1121.mov

Bash script/command to print out date 5 min before/after

I need to somehow use the date command in bash or another utility to print out the date and time, 5 minutes before and 5 minutes after a given value.
For example:
input:
Thu Dec 19 14:10
output:
Thu Dec 19 14:05 Thu Dec 19 14:10 Thu Dec 19 14:15
I see that the date command can be used to do this on the current date/time, can it be used with a passed value, i.e. not the current date/time?
You can achieve this, for the current time, by typing.
$ date --date='5 minutes ago'; date; date --date='5 minutes'
Qui Dez 19 16:09:17 BRST 2013
Qui Dez 19 16:14:17 BRST 2013
Qui Dez 19 16:19:17 BRST 2013
To use a specific date (ex 1978/01/10).
$ date --date='1978-01-10 + 5 minutes'
Ter Jan 10 00:05:00 BRT 1978
With GNU date, you can do a simple form of date/time arithmetic with the argument to the --date option:
$ date --date 'now + 5 minutes'
With BSD date (at least, the version that ships with Mac OS X), the -v option allows you to do something similar:
$ date -v +5M
$ date -v -5M
If you're using bash under linux, you can use the -d parameter to perform date manipulation on an existing date:
Get the EPOCH time for the date in question:
EPOCH=$(date -d 'Thu Dec 19 14:10' '+%s')
This gives you the time, in seconds, since the EPOCH (typically 01/01/1970)
Now you can use simple math to subtract or add 5 minutes (in seconds) to the EPOCH time
NEW_EPOCH=$(($EPOCH - 300))
obviously, there are 300 seconds in 5 minutes
Now convert this NEW_EPOCH back into a human readable date
NEW_DATE=$(date -d "1970-01-01 ${NEW_EPOCH} sec")
NOTE that this only works on unix systems which support the date -d option (i.e. Linux)
If you want to do this for the current time +/-5 minutes and you use Bash 4.2 or newer, you can do it without external tools:
$ printf -v now '%(%s)T'
$ echo "$now"
1516161094
$ f='%a %b %d %R'
$ printf "%($f)T %($f)T %($f)T\n" "$((now-300))" "$now" "$((now+300))"
Tue Jan 16 22:46 Tue Jan 16 22:51 Tue Jan 16 22:56
The %(datefmt)T formatting string of printf allows to print date-time strings. If the argument is skipped (like here) or is -1, the current time is used.
%s formats the time in seconds since the epoch, and -v now stores the output in now instead of printing it.
f is just a convenience variable so I don't have to repeat the formatting string for the output three times.
Since the argument for this usage of printf has to be in seconds since the epoch, you're stuck with external tools to convert an input string like Thu Dec 19 14:10 into that format and you'd replace
printf -v now '%(%s)T'
with, for example, any of
now=$(date '+%s' -d 'Thu Dec 19 14:10') # GNU date
now=$(date -j -f '%a %b %d %T' 'Thu Dec 19 14:10' '+%s') # macOS date

Resources