bash sub-stringing to put creation dates on .jpg and other files

July 10, 2011

to rename all my camera photos I need some string functions,
to put the creation date on all the jpg files I need … jhead.

Linux does not keep the creation date; it keeps the modified date. The date on the file changes as soon as you copy or edit it, like with a photo editor.

if you have not modified the image so that the creation and modified dates are still the same, you can put the modified date on the front of the file name this way:
given image names like DSC00123.jpg

for fi in  *.jpg
   do
      num=${fi:4:5}
      fdate=stat -c %y $fi | cut -d ' ' -f1
      fdt=${fdate:0:4}${fdate:5:2}${fdate:8:2}   # (remove hyphens)
      rename  DSC0  XX-$fdt-  DSC0${num}*
   done

“Bash, an acronym for “Bourne-Again shell” is a pun on Stephen Bourne’s now classic Bourne shell. Bash has become a de facto standard for shell scripting on most flavors of UNIX.”

#, ##, and %, %%

$ MYVAR=foodforthought.jpg
$ echo ${MYVAR#*fo}
odforthought.jpg
$ echo ${MYVAR##*fo}
rthought.jpg

${MYVAR#*fo}
bash took MYVAR, found the first instance of “fo”, and returned what was after it.
${MYVAR##*fo}
found the last instance of “fo”, and returned what was after it.

$ MYFOO=”chickensoup.tar.gz”
$ echo ${MYFOO%.*}
chickensoup.tar
$ echo ${MYFOO%%.*}
chickensoup

${MYFOO%.*}
in reverse, found the first period, and returned what preceded it.
(found the lastperiod, and returned what was before it)
${MYFOO%%.*}
in reverse, found the last period, and returned what preceded it.
(found the first period, and returned what was before it)

$ foo=/tmp/my.dir/filename.tar.gz
We can use these expressions:
path = ${foo%/*}
To get: /tmp/my.dir (like dirname)
file = ${foo##*/}
To get: filename.tar.gz (like basename)
base = ${file%%.*}
To get: filename
ext = ${file#*.}
To get: tar.gz

“It’s important to understand that these use shell “globbing” rather than “regular expressions” to match these patterns.”

“we can do quite a bit of string parsing and manipulation directly within bash — which will allow our shell scripts to run faster with less overhead.”

Substring by numerical position

$ MYFOO=”0123456789abc”
$ echo ${MYFOO:7}
789abc
$ echo ${MYFOO:7:4}
789a
$ echo ${MYFOO:0:4}
0123

${MYFOO:7}
all the characters after the 7th
${MYFOO:7:4}
the first 4 characters after the 7th
${MYFOO:0:4}
the first 4 characters

String Length

${#string}

expr length $string

expr “$string” : ‘.*’

$ stringZ=abcABC123ABCabc     # length=15
$ echo ${#stringZ}
15
$ echo `expr length $stringZ`
15
$ echo `expr “$stringZ” : ‘.*’`
15

String Position

echo `expr index “$stringZ” C12`
6

Numerical position in $string of first character in $substring that matches.

Truncate

#!/bin/bash
# quickly convert html filenames to htm
# only handles file extensions, not file names
for i in *.html; do
if [ -f ${i%l} ]; then
echo “${i%l} already exists”
else
mv $i ${i%l}
fi
done


now, how to use the functions to

How to put the creation date on your jpg’s and videos

1. install jhead: yum install jhead … or … use the gnome tool, system -> administration -> add/remove software

2. create a text file containing something like

#!/bin/bash
for fi in *.jpg
  do
    jhead -nXX-%Y-%m-%d-%f  $fi  # XX- being any prefix you want on the file name 
    #jhead -nXX-%Y%m%d-%f  $fi   # or, switch to this one without the dashes
  done

If your jpg’s are named
00123.jpg
00124.jpg
00125.jpg
etc.

after the script above, they will be something like
XX-2011-07-06-00123.jpg
XX-2011-07-07-00124.jpg
XX-2011-07-08-00125.jpg
if they were taken on July 6th, 7th, and 8th, for instance.

If your jpg’s are named
DSC00123.jpg
DSC00124.jpg
DSC00125.jpg

they will now be
XX-2011-07-06-DSC00123.jpg
XX-2011-07-07-DSC00124.jpg
XX-2011-07-08-DSC00125.jpg

and, you could remove the “DSC” with the rename command:
rename ‘-DSC0’ ‘-‘ *-DSC0*

to result in this:
XX-2011-07-06-00123.jpg
XX-2011-07-07-00124.jpg
XX-2011-07-08-00125.jpg

my camera also takes videos (and keeps them in another folder)

Each video is in 2 files:
1. a thumbnail .jpg, for some reason, named .THM and
2. a .mp4, the video.
Both are prefixed with MAQ or MAH, instead of DSC. I changed them all to DSC to make the rest of the script easier. I am going to remove the 3 letters anyway, regardless. I also lower cased the .jpg’s that were not already, to make that consistent also.

to rename them, I wrote this:

rename .THM .jpg *.THM
rename MAQ DSC MAQ*
rename MAH DSC MAH*
rename .JPG .jpg *.JPG
rename .MP4 .mp4 *.MP4

# some cameras, like windows make the images executable. THEY ARE NOT! And it is a security risk to accept images that are. I fix that.

chmod 644 *.jpg
chmod 644 *.mp4

Here is the hard part, the thumbnail .jpg has the creation date on it but not the .mp4. I need to get the date from the jpg and put it on the mp4.

for fi in DSC0*.jpg
  do
    jhead -nGr-%Y%m%d-%f  $fi
    rename '-DSC0' '-'  *-DSC0*
  done
 #rename '-DSC0' '-' *-DSC0*

for MP in DSC0????.mp4
  do
    num=${MP:4:5}
    #echo $MP $num 
    for jpg in *${num}jpg 
      do
        #echo $jpg  ${jpg%%jpg}
        mv  $MP  ${jpg%%jpg}mp4
      done
  done

A listing of commands within parentheses starts a subshell.

Variables inside parentheses, within the subshell, are not visible to the rest of the script. The parent process, the script, cannot read variables created in the child process, the subshell.

a=123
( a=321; )

echo “a = $a” # a = 123
# “a” within parentheses acts like a local variable.

Modulo (remainder of a division)
let “z = 5 % 3”
echo $z
2

# Assignment
a=879
echo “The value of \”a\” is $a.”

# Assignment using ‘let’
let a=16+5
echo “The value of \”a\” is now $a.”

#!/bin/bash
# int-or-string.sh

a=2334 # Integer.
let “a += 1”
echo “a = $a ” # a = 2335
echo # Integer, still.

uninitialized variables

echo “$uninitialized” # (blank line)
let “uninitialized += 5” # Add 5 to it.
echo “$uninitialized” # 5

# Conclusion:
# An uninitialized variable has no value,
#+ however it evaluates as 0 in an arithmetic operation.

command substitution

a=`echo Hello!` # Assigns result of ‘echo’ command to ‘a’ …

Leave a Reply

We try to post all comments within 1 business day