Using quotes within command substitution - bash

I don't understand why this works
dateCMD=$(date -d "4 hours ago")
echo $dateCMD
Tue Oct 18 02:20:34 AEDT 2016
and this works
dateCMD="date"
$dateCMD
Tue Oct 18 06:23:19 AEDT 2016
But not this
dateCMD='date -d "4 hours ago"'
$($dateCMD)
date: extra operand ‘ago"’
How can I get this last case to work?

Short answer: you don't want to do that. Use a function instead.
dateCMD () {
date -d "4 hours ago"
}
dateCMD
Long answer: quotes in the value of a parameter are not "syntactic" quotes; they are just regular data. When you write
dateCMD='date -d "4 hours ago"'
$($dateCMD)
It is evaluated as follows:
$dateCMD expands to date -d "4 hours ago"
The expansion is split into the words date, -d, "4, hours, and ago". The quotes are not treated specially.
The first word, date, is treated as the command with the rest of the words passed as distinct arguments.
date treates "4 as the argument to the -d option. Since date takes only one additional positional argument (the format string), it takes hours as that argument, then complains that ago is an extra operand.

Use BASH array to make it work:
dateCMD=(date -d "4 hours ago")
"${dateCMD[#]}"
Mon Oct 17 11:38:54 EDT 2016
I was typing explanation on your quoting problem but then I noticed very well explained answer from #chepner so avoided adding redundant information in my answer.

To get the last case to work, you can use eval
Of course you should not use eval on any user input without completely validating that input.
dateCMD='date -d "4 hours ago"'
eval $dateCMD

Related

Unix shell script Date format

I'm looking for a Date for like Mar 1 (i.e. space+padded space of day of month)
This is giving correct format => Mar 1
date -d "yesterday 13:00" +"%b %e"
However, when I try to put into a variable, the extra added space is missing.
DATE=`date -d "yesterday 13:00" +"%b %e"`
echo $DATE gives=> Mar 1 (only padded space getting)
Any idea how to get that => Mar 1 (i.e. space+padded space of day of month)
For lots of details, see When to wrap quotes around a shell variable? or I just assigned a variable, but echo $variable shows something else
Basically, you need to quote your variable when you use it:
echo "$DATE"

Bash date query stopped working

A few months ago I wrote a some bash to get dates. I needed these dates as facts in an ansible script to later use them to get data from the database. This worked fine until today here is the code:
- name: Set date variables
set_fact:
first_day_last_month: "{{lookup('pipe','date -d \"-1 month -$(($(date +%d)-1)) days\" +%Y-%m-%d')}}"
last_day_last_month: "{{lookup('pipe','date -d \"$(date +%Y-%m-01) -1 day\" +%Y-%m-%d')}}"
first_day_current_month: "{{lookup('pipe','date -d \"-$(($(date +%d)-1)) days\" +%Y-%m-%d')}}"
last_day_current_month: "{{lookup('pipe','date -d \"-$(date +%d) days +1 month\" +%Y-%m-%d')}}"
However when I run this now I get an error:
/bin/sh: 1: arithmetic expression: expecting EOF: "08-1"
I tried debugging it on the bash command line:
seven#monstermachine:~$ echo $(date -d "-$($(date +%d)-1)")
08-1: command not found
Mit Nov 8 00:00:00 CET 2017
but until now i'm not getting it fixed.
Anyone have any idea?
%d is zero padded which gives base eight or octal number and 08 is not a valid octal number which thows an error.
What you need is something like :
echo $(date -d "$(($(date +%e)-1))")
Wed Nov 8 07:00:00 IST 2017
Note %e will cause date to be space padded which is equivalent to %_d.
To perform a mathematical expression use $(( expression )) format. I didn't understand the actual logic behind your code, so make sure your code fits the logic.
To get first_day_current_month in your script, you could use:
date +%Y-%m-01
similar for the others, for example to get last_day_last_month:
date -d `date +%Y-%m-01`"-1 day" +%Y-%m-%d
Thanks for the help I will make use of both of your answers to come up with a better working command.

Simultaneous commands

I have a script:
#!/bin/bash
date +%T &
Hours=`date +"%H"` &
Minutes=`date +"%M"` &
Seconds=`date +"%S"`
echo "$Hours:$Minutes:$Seconds"
The objective is to echo date two times and then take out hours, minutes, seconds and calculate how many seconds elapsed between those two commands. So my solution is to write hours, minutes, seconds into variables, then work with those variables.
Problem: echo only echoes seconds which means my interpretation of & is wrong.
How can I fix the & problem? I need those commands to run simultaneously so I can check.
date +%s apparently won't work on certain inputs like:
Wed Mar 4 10:34:59 2015
Wed Mar 4 10:35:08 2015
Will give result of 00:00:01 instead of 00:00:09 or:
Wed Mar 4 10:34:59 2015
Wed Mar 4 17:43:08 2015
will give the result of 12:13:14 instead of 07:08:09. Is it true? Or can I use date +%s and then decrease those two outputs?
You don't need to run the commands simultaneously. Run just one command:
read hours minutes seconds < <( date '+%H %M %S' )
But it can be even simpler: just use the +%s format to get number of seconds since the epoch. You'll get two numbers you can safely subtract.
#!/bin/bash
start=$(date +%s)
sleep 10
end=$(date +%s)
echo The command took $(( end - start )) seconds.
The easiest way is to use the shell variable $SECONDS.
Each time this parameter is referenced, the number of seconds since shell invocation is returned. If a value is assigned to SECONDS, the value returned upon
subsequent references is the number of seconds since the assignment plus the
value assigned. If SECONDS is unset, it loses its special properties, even if
it is subsequently reset.
the main problem is that in this case the command date will be executed tree times to get the values..
so you don't need to execute the commad date too many times
#!/bin/bash
#date +%T &
function myTime(){
now=`date +"%H:%M:%S" &`
}
myTime
s1=`echo $now | cut -d":" -f2`
myTime
s2=`echo $now | cut -d":" -f2`
echo "s1[$s1] - s2[$s2]"
Then you can apply your rules to verify the time elapsed
Regards
Claudio

Using a different date in a shell script

SO, I have a whole script that runs every hour with a date as input. Normally, it takes the current time, but now I need it to run for an interval of time in the past, every hour as well.
What I've done so far is:
DEFINING THE OLD DATE
8 start_date=20131218
9 num_hours=5
10 for i in `seq 1 $num_hours`
11 do
12 date=`date -d "${start_date}+${i} hours"`
13 echo $date # Use this however you want!
14
.
.
.
25 done
The starting date is Dec 18, 2013 and then in each iteration it should give me one more hours from the starting time. This part I found it in another article here and it works. The problem comes when I do
echo $(date)
it prints the current time instead of the time that I previously defined. Of course any other variable that I define from the date has the values from the current time. For instance,
18 datestamp=$(date +%F)
19 hourstamp=$(date +%H)
I'm new in shell programming and I have no idea what to do. Any help?
Thanks in advance.
What you want is this:
18 datestamp=$(date -d "${start_date}+${i} hours" +%F)
19 hourstamp=$(date -d "${start_date}+${i} hours" +%H)
As #BMW said, try to avoid use the date as a variable name to avoid ambiguity.
$(date) will run the command date and export the result.
so when echo it, it will return current date/time.
Second, date is the unix command, avoid to use it as a variable. so this will fix your issue"
DATE=`date -d "${start_date}+${i} hours"`
echo $DATE

Getting date of X days ago in bash script, using argument variable

I'm trying to calculate the date for a dynamic number of days ago in a bash script.
This is what I've done -
#!/bin/bash
STAMP=`date --date='$1 day ago' +%y%m%d`
but when running myscript 2, it says -
date: invalid date `$1 day ago'
How can I use my argument value in this formula?
It works if ' is replaced with " into this command on the script -
STAMP=`date --date="$1 day ago" +%y%m%d`
The clue was the two different character ` and ' used in the error response -
date: invalid date `$1 day ago'
An expert in bash scripting (not me) can probably explain why this has happen.
It's because variable substitution wouldn't happen in single quotes, i.e. '$1' wouldn't expand but "$1" would.
As such, saying
STAMP=`date --date="$1 day ago" +%y%m%d`
or
STAMP=$(date --date="$1 day ago" +%y%m%d)
would work.

Resources