11

I am trying to create a raspberry pi spy cam bug. I am trying to make it so the new file created for the various processes come up with

NOW=`date '+%F_%H:%M:%S'`;

which works fine. but it requires an echo to update the time $NOW is also in the /home/pi/.bashrc file Same issue, does not update wo

. ~/.bashrc 

I found on this forum here and it works:

#! /bin/bash

NOW=`date '+%F_%H:%M:%S'`;

filename="/home/pi/gets/$NOW.jpg"

raspistill -n -v -t 500 -o $NOW.jpg;

echo $filename;

I don't get how it works bc it's before o the output of raspistil and in quotes.

Thank you all in advance!!!

Reckless Liberty
  • 133
  • 2
  • 2
  • 7
  • found the forum I lifted that code from, it's this one. https://unix.stackexchange.com/questions/57590/appending-a-current-date-from-a-variable-to-a-filename toward the bottom VVV – Reckless Liberty Mar 05 '18 at 11:10

4 Answers4

28

When you do

NOW=`date '+%F_%H:%M:%S'`

or, using more modern syntax,

NOW=$( date '+%F_%H:%M:%S' )

the variable NOW will be set to the output of the date command at the time when that line is executed. If you do this in ~/.bashrc, then $NOW will be a timestamp that tells you when you started the current interactive shell session.

You could also set the variable's value with

printf -v NOW '%(%F_%H:%M:%S)T' -1

if you're using bash release 4.2 or later. This prints the timestamp directly into the variable without calling date.

In the script that you are showing, the variable NOW is being set when the script is run (this is what you want).

When the assignment

filename="/home/pi/gets/$NOW.jpg"

is carried out, the shell will expand the variable in the string. It does this even though it is in double quotes. Single quotes stops the shell from expanding embedded variables (this is not what you want in this case).

Note that you don't seem to actually use the filename variable in the call to raspistill though, so I'm not certain why you set its value, unless you just want it outputted by echo at the end.

In the rest of the code, you should double quote the $NOW variable expansion (and $filename). If you don't, and later change how you define NOW so that it includes spaces or wildcards (filename globbing patterns), the commands that use $NOW may fail to parse their command line properly.

Compare, e.g.,

string="hello * there"

printf 'the string is "%s"\n' $string

with

string="hello * there"

printf 'the string is "%s"\n' "$string"

Related things:

Kusalananda
  • 333,661
  • thank you for the info, I have been reading up on printf. i compared the 2 lines, i do not get why the not doubled quoted listed the files in that directory. I get the substitutions, like truffles. – Reckless Liberty Mar 06 '18 at 09:13
  • @RecklessLiberty That's the whole point of quoting the variable expansion. Without the quote, the * will perform a filename glob and be expanded to the names in the current directory. – Kusalananda Mar 06 '18 at 09:15
  • that's weird, it did the hello and there too. do you know where I could read up on this? why did it go for the file names? – Reckless Liberty Mar 06 '18 at 09:20
  • @RecklessLiberty An unquoted * expands to all names in the current directory. If you had used *.txt, it would have expanded to the names that ended with .txt. See e.g. https://en.wikipedia.org/wiki/Glob_(programming) and https://unix.stackexchange.com/questions/131766/why-does-my-shell-script-choke-on-whitespace-or-other-special-characters – Kusalananda Mar 06 '18 at 09:26
  • bc it's echo'd in plain text!!! wo the quotes each character will be interpreted. Got it, it clicked, that did it. Thanks again!!! – Reckless Liberty Mar 07 '18 at 11:20
2

This answer is a few practical pointers:

  1. Don't use characters such as : for naming files! Before using any non-alphanumeric character, ask yourself whether it could possibly mess up any future script or command line one-liner. Use a dash or underline instead, or just squish the elements together with no delimiter.

  2. Since your question indicates that you are using bash, you should know that recent versions of that shell include a native version of date-spec output withing the shell's own printf command. Use it like so:

    printf "%(%F_%H%M%S)T"
    

    or

    NOW=$(printf "%(%F_%H%M%S)T")
    

    or even

    printf -v NOW "%(%F_%H%M%S)T"
    

    (which avoids forking an extra process)

  3. The bash version of printing a date-spec has the additional advantage of two features not present otherwise: "-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." For example,

    printf -v THEN "%(%F_%H%M%S)T" -2
    
user1404316
  • 3,078
  • That's a nice feature of Bash's printf, but I don't really feel convinced about your first point. Why would : (or non-alphanumeric characters in general) be bad in filenames? – Kusalananda Mar 05 '18 at 12:47
  • 2
    @Kusalananda - What initially comes to mind are two cases: 1) hostnames: if you have a colon and it gets sucked up by a glob into a script for use with something like rsync, scp, or even find, it can end up crashing the script looking for a network hostname. 2) Lists of things, such a folders in your $PATH or in an ldd pathh list, use the colon as a delimiter. – user1404316 Mar 05 '18 at 13:20
  • @Kusalananda I answered (I think) a question on here a while ago where somebody couldn't extract an archive because the target filesystem didn't support : in filenames. That seems like a good reason to avoid them – Fox Mar 05 '18 at 15:58
  • All valid points under specific circumstances. But on the same premises, you could also say that you should adhere to Windows naming conventions in case the files were to be transferred to such a system. – Kusalananda Mar 05 '18 at 16:01
  • What if you do port to windows or happen to be accessing the files in an application that doesn't support the characters at a later date. You can prevent those situations from coming back and biting you by just sticking to best practices in the first place. Of course that is your choice to make either way, but I know how I would be working it. – CodingInTheUK Apr 06 '19 at 07:55
  • Note that that %T format was copied from ksh93 / ast-open (where it's much more advanced as it allows a wide variety of date/time/offsets specifications, where bash only allows epoch seconds or -1, -2). – Stéphane Chazelas May 06 '20 at 19:32
2

Since Bash 5: if the number of seconds since epoch (1970-01-01 00:00:00) satisfies your needs, you can get that timestamp from two predefined variables:

# Seconds since epoch
echo ${EPOCHSECONDS}

Seconds with microsecond precision since epoch

echo ${EPOCHREALTIME}

Microseconds since epoch

echo ${EPOCHREALTIME/./}

Miliseconds since epoch

echo $(( ${EPOCHREALTIME/./} / 1000 ))

t0r0X
  • 819
0

I was looking for a way to have the time actually update while the script was running in bash to prevent stompage on files I was updating more than once (yes yes) and still have it simply be "assigned" to a variable. Pretty sure this isn't an issue in Perl, but it's been a while.

user1404316's answer doesn't work for me with bash 4.2 on RHEL.

Here's how I solved this in bash 4.2:

#!/bin/bash
now=$(printf "%(%F_%H%M%S)T")
echo printf: ${now}
sleep 1
echo printf after 1: ${now}

# Tried backticks here too :(    
then="$(date +%s)"

# This is your now date
now() { date +%s; }

# This is how you use it    
filename="something.$(now)"

echo "now ${filename}"
filename="something.${then}"
echo "then ${filename}"
sleep 1
filename2="something.$(now)"
echo "now ${filename2}"
filename2="something.${then}"
echo "then ${filename2}"

and the output:

printf: 1969-12-31_190000
printf after 1: 1969-12-31_190000
now something.1588791459
then something.1588791459
now something.1588791460
then something.1588791459

Hope this helps someone :)

  1. Have to give some credit to user unknown @ askubuntu.com as I did review that code while trying to find the right solution.
Pavman
  • 1