Interpolate bash command in Heroku scheduler - node.js

How can I interpolate a bash command in a Heroku scheduler command?
I have a command that runs everyday & it takes a day. Now I want to make it dynamic using bash date command e.g
cli "$(date --date "7 day ago")"
For today it will be Sat Apr 27 22:36:46 +06 2019 and for tomorrow it will be Sun Apr 28 22:36:46 +06 2019.
How can I achieve this?

Stack Overflow's syntax highlighting makes the problem pretty clear. You're nesting double quotes inside double quotes without escaping them:
cli "$(date --date "7 day ago")"
This is interpreted as three arguments:
"$(date --date "7
day
ago")"
Replace the innner ones with single quotes and it should work:
cli "$(date --date '7 day ago')"
Another alternative would be to escape the inner quotes with backslashes, but IMO using single quotes is more readable.

Related

Can't use variable in date

I'm trying to convert a string into seconds using date +%s, but everytime i get this error:
deadline='Monday August 2 10:45:25 AM GMT'
date -d $deadline +%s
date: extra operand'2'
But if I don't use a variable this command works fine
date -d 'Monday August 2 10:45:25 AM GMT' +%s
1627901012
Can somebody explain why this command doesn't work with my variable?
why this command doesn't work with my variable?
Unquoted variable expansions undergo word splitting.
After expansion, you are executing:
date -d Monday August 2 10:45:25 AM GMT +%s
Monday is the argument for -d, August is the one argument that date takes. Then the rest August 2 10:... are just invalid positional arguments for date, so the tool exits with an error that too many arguments were passed.

Find number of days from two dates using shell

I would like to find number of days from two given dates as 25 Aug 2017 and 05 Sep 2017.
My script:
start='2017-08-25';
end='2017-09-05';
ndays=(strtotime($end)- strtotime($start))/24/3600;
echo $ndays
When run this script, I am getting following error messages.
Line 3: syntax error near unexpected token `('
Desire output value:
10
No shell that I am aware of has any tools for working with dates. At the very least, you need an external tool like date to convert your dates to an intermediate form, like the number of seconds since some fixed point in time (Unix uses Jan 1, 1970), then do your calculation with those values before processing the result further.
Assuming the use of GNU date from the Linux tag, you would do something like
start='2017-08-25'
end='2017-09-05'
start_seconds=$(date +%s --date "$start" --utc)
end_seconds=$(date +%s --date "$end" --utc)
ndays=$(( (end_seconds - start_seconds) / 24 / 3600 ));
echo $ndays
Note that since most shells only support integer arithmetic, this won't give you an exact number of days.
You can use date to convert each date to epoch seconds, then use arithmetic expansion to do the subtraction and conversion back to days:
#! /bin/bash
start='2017-08-25';
end='2017-09-05';
diff=$(date -d $end +%s)-$(date -d $start +%s)
echo $(( ($diff) / 60 / 60 / 24 )) # 11

Print variable containing command

Can somebody tell me how to print variable and not executing it in bash? I mean:
bash#bash $ cat script.sh
#!/bin/bash
echo $1
bash#bash $ ./script.sh "`date`"
Sat Sep 20 18:42:19 CEST 2014
I don't want to get:
Sat Sep 20 18:35:37 CEST 2014
I want to get output:
date
I'm interested in how to prevent executing sent commands to script.
It looks like you are trying to prevent code from being injected into your script. The problem with echo $1 is that the contents of $1 are being evaluated by the shell. In order to avoid that, you need to wrap $1 in double quotes in your script:
#!/bin/bash
echo "$1"
Testing it out:
$ ./script.sh '`date`'
`date`
The problem in your question is that you are using double quotes around "date", so the expansion has occurred before your script is run. You can use set -x to see the difference:
$ set -x
$ ./script '`date`'
+ ./script '`date`'
`date`
$ ./script "`date`"
++ date # date is being run
+ ./script 'Sat Sep 20 18:01:32 BST 2014' # result is passed to your script
Sat Sep 20 18:01:32 BST 2014
There is nothing you can do about this.
I think the following section of my original answer is still relevant, so I'll leave it in.
Different types of quotes in bash
Backticks (which you have used in your question) are an old-fashioned way of capturing the output of executing a command. The more modern syntax is $( ), so if you wanted to store the current date in a variable you could do d=$(date). Single quotes are used for literal strings, so echo '$d' would output $d, not the value of the variable. Variables inside double quotes are expanded, so echo "$d" would output the value of the variable $d. It is always a good idea to wrap your variables in double quotes to prevent word splitting and glob expansion.
Replace the backticks from var1 with single quotes:
var1='date'
var2="echo $var1"
echo $var2

Bash Script Help - Tar not working with variables?

Am trying to backup set folders based on yesterday's date by using a bash script and CRON task.
The folder structure of the site is like this:
/home/admin/domains/mysite.com/public_html/media/2014/March
And I would want to back-up that folder to this file:
/home/admin/domains/mysite.com/public_html/bk/mediabackup-March-2014.tar.gz
So created this script:
#!/bin/bash
NOW=$(date -d "12 hours ago" '+%m')
NOWYEAR=$(date -d "12 hours ago" '+%Y')
MONTHS=(Dummy January February March April May June July August September October November December)
NOWMONTH=${MONTHS[3]}
FILE="/home/admin/domains/mysite.com/public_html/bk/mediabackup-$NOWMONTH-$NOWYEAR.tar.gz"
PATH="/home/admin/domains/mysite.com/public_html/media/$NOWYEAR/$NOWMONTH"
tar -zcvf $FILE $PATH
When I run this script though shell, I would do this:
bash script.sh
And it could come up "command not found" - not sure why it's not working?
Any help would be great, thanks :)
By overriding the shell's built-in PATH variable, you are causing it to not find the tar command. Use another variable name, and generally, refrain from using uppercase variable names.
No need to hard-code the month names:
read year month < <(date -d "12 hours ago" "+%Y %B")
echo "$month-$year"
March-2014

Get yesterday's date in bash on Linux, DST-safe

I have a shell script that runs on Linux and uses this call to get yesterday's date in YYYY-MM-DD format:
date -d "1 day ago" '+%Y-%m-%d'
It works most of the time, but when the script ran yesterday morning at 2013-03-11 0:35 CDT it returned "2013-03-09" instead of "2013-03-10".
Presumably daylight saving time (which started yesterday) is to blame. I'm guessing the way "1 day ago" is implemented it subtracted 24 hours, and 24 hours before 2013-03-11 0:35 CDT was 2013-03-09 23:35 CST, which led to the result of "2013-03-09".
So what's a good DST-safe way to get yesterday's date in bash on Linux?
I think this should work, irrespective of how often and when you run it ...
date -d "yesterday 13:00" '+%Y-%m-%d'
Under Mac OSX date works slightly different:
For yesterday
date -v-1d +%F
For Last week
date -v-1w +%F
This should also work, but perhaps it is too much:
date -d #$(( $(date +"%s") - 86400)) +"%Y-%m-%d"
If you are certain that the script runs in the first hours of the day, you can simply do
date -d "12 hours ago" '+%Y-%m-%d'
BTW, if the script runs daily at 00:35 (via crontab?) you should ask yourself what will happen if a DST change falls in that hour; the script could not run, or run twice in some cases. Modern implementations of cron are quite clever in this regard, though.
Here a solution that will work with Solaris and AIX as well.
Manipulating the Timezone is possible for changing the clock some hours.
Due to the daylight saving time, 24 hours ago can be today or the day before yesterday.
You are sure that yesterday is 20 or 30 hours ago. Which one? Well, the most recent one that is not today.
echo -e "$(TZ=GMT+30 date +%Y-%m-%d)\n$(TZ=GMT+20 date +%Y-%m-%d)" | grep -v $(date +%Y-%m-%d) | tail -1
The -e parameter used in the echo command is needed with bash, but will not work with ksh.
In ksh you can use the same command without the -e flag.
When your script will be used in different environments, you can start the script with #!/bin/ksh or #!/bin/bash. You could also replace the \n by a newline:
echo "$(TZ=GMT+30 date +%Y-%m-%d)
$(TZ=GMT+20 date +%Y-%m-%d)" | grep -v $(date +%Y-%m-%d) | tail -1
date -d "yesterday" '+%Y-%m-%d'
To use this later:
date=$(date -d "yesterday" '+%Y-%m-%d')
you can use
date -d "30 days ago" +"%d/%m/%Y"
to get the date from 30 days ago, similarly you can replace 30 with x amount of days
Just use date and trusty seconds:
As you rightly point out, a lot of the details about the underlying computation are hidden if you rely on English time arithmetic. E.g. -d yesterday, and -d 1 day ago will have different behaviour.
Instead, you can reliably depend on the (precisely documented) seconds since the unix epoch UTC, and bash arithmetic to obtain the moment you want:
date -d #$(( $(date +"%s") - 24*3600)) +"%Y-%m-%d"
This was pointed out in another answer. This form is more portable across platforms with different date command line flags, is language-independent (e.g. "yesterday" vs "hier" in French locale), and frankly (in the long-term) will be easier to remember, because well, you know it already. You might otherwise keep asking yourself: "Was it -d 2 hours ago or -d 2 hour ago again?" or "Is it -d yesterday or -d 1 day ago that I want?"). The only tricky bit here is the #.
Armed with bash and nothing else:
Bash solely on bash, you can also get yesterday's time, via the printf builtin:
%(datefmt)T
causes printf to output the date-time string resulting from using
datefmt as a format string for strftime(3). The corresponding argu‐
ment is an integer representing the number of seconds since the
epoch. Two special argument values may be used: -1 represents the
current time, and -2 represents the time the shell was invoked.
If no argument is specified, conversion behaves as if -1 had
been given.
This is an exception to the usual printf behavior.
So,
# inner printf gets you the current unix time in seconds
# outer printf spits it out according to the format
printf "%(%Y-%m-%d)T\n" $(( $(printf "%(%s)T" -1) - 24*3600 ))
or, equivalently with a temp variable (outer subshell optional, but keeps environment vars clean).
(
now=$(printf "%(%s)T" -1);
printf "%(%Y-%m-%d)T\n" $((now - 24*3600));
)
Note: despite the manpage stating that no argument to the %()T formatter will assume a default -1, i seem to get a 0 instead (thank you, bash manual version 4.3.48)
You can use:
date -d "yesterday 13:55" '+%Y-%m-%d'
Or whatever time you want to retrieve will retrieved by bash.
For month:
date -d "30 days ago" '+%Y-%m-%d'
As this question is tagged bash "DST safe":
And using fork to date command implie delay, there is a simple and more efficient way using pure bash built-in:
printf -v tznow '%(%z %s)T' -1
TZ=${tznow% *} printf -v yesterday '%(%Y-%m-%d)T' $(( ${tznow#* } - 86400 ))
echo $yesterday
This is a lot quicker on more system friendly than having to fork to date.
From bash version 5.0, there is a new variable $EPOCHSECONDS
printf -v tz '%(%z)T' -1
TZ=$tz printf -v yesterday '%(%Y-%m-%d)T' $(( EPOCHSECONDS - 86400 ))
echo $yesterday

Resources