Batch change creation dates based on file names

I have files that are named like "12. 2017-04-11 063118.jpg", and "123. 2016-09-05 115236.jpg". The number to the left of the "." counts up like an index from 1 to 1400.

How can I set the creation date and time according to how it is specified in the file name for all 1400 files I have in the folder. I know the command touch -t changes the creation date, but I do not know how to extract the date/time information from the file name and put this into a command so this is done automatically for all files.

I know from another thread that the below should work. but i don't know how to tweak the text conversion to correctly apply to my file naming convention.

for f in *; do
    t=$(echo $f | sed -E 's/([A-z]*-)|([ ,;])|(\..*)//g' | sed -E 's/(.*)(..)/\1.\2/')
    touch -t $t "$f"
done

The above code works if my file naming convention is like "clip-2014-01-31 18;50;15.mp4", however, I have a different format: "12. 2017-04-11 063118.jpg", and "123. 2016-09-05 115236.jpg". Does anyone know how to tweak the sed function command to process the conversion into the correct format for touch?


Bash parameter expansion will work:

for f in *; do
    t="${f#* }"
    t="${t%%.*}"
    t="${t:0:4}${t:5:2}${t:8:2}${t:11:2}${t:13:2}.${t:15:2}"
    touch -t $t "$f"
done
  • t="${f#* }" strips off the part before (< the first space)
  • t="${t%%.*}" strips off the part after .
  • t="${t:0:4}${t:5:2}${t:8:2}${t:11:2}${t:13:2}.${t:15:2}" formats the content of the date variable (t) for touch which requires the following format: [[CC]YY]MMDDhhmm[.SS].

And as one-liner:

for f in *; do t="${f#* }"; t="${t%%.*}"; t="${t:0:4}${t:5:2}${t:8:2}${t:11:2}${t:13:2}.${t:15:2}"; touch -t $t "$f"; done

Since you asked how you could tweak what you already had, you'd change the capture groups in the first occurrence of sed to match the new pattern, while leaving the second sed as is. Also double quote the $f in echo "$f" |.

Note however, I've eliminated the use of the pipe and second occurrence of sed, | sed ..., by use of the -e option, which appends the editing commands into a list of commands.

for f in *; do
    t=$(echo "$f" | sed -E -e 's/(.*\. )|([-])|([ ])|(\..*)//g' -e 's/(.*)(..)/\1.\2/')
    touch -t $t "$f"
done

Here it is tweaked as a one-liner:

for f in *; do t=$(echo "$f" | sed -E -e 's/(.*\. )|([-])|([ ])|(\..*)//g' -e 's/(.*)(..)/\1.\2/'); touch -t $t "$f"; done

Here's an explication of the capture groups in the first sed editing command:

enter image description here

Which leaves you with 20160905115236 and when processed by the second sed editing command, it becomes 201609051152.36 as (.*)(..) breaks it down to two capture groups, the second containing the last two characters and the first containing everything else. The . gets placed in the substitution portion having back-referenced /\1.\2/ both capture groups.