I have a lot of files where filename contains file creation date (IMG_YYYYMMDD_hhmmss.jpg):


but actual file creation date is different. I need to set it back to date from a filename.

I know how to change the date of all files to current date:

find -type f -exec touch {} \;

and how to parse the file name to get the date in the proper format for touch -t

echo IMG_20171015_133516.jpg | awk -F_ '{print $2 substr($3,0,4) "." substr($3,5,2)}'

But I have no idea how to combine these commands together to change all files.

find -type f -exec touch -t"$(echo '{}' | awk -F_ '{print $2 substr($3,0,4) "." substr($3,5,2)}')" {} \;


touch: invalid date format ‘.’

1 Answer 1


In the shell:

for f in ./IMG_*.jpg ; do 
    touch "$f" -t "${t:4:8}${t:13:4}.${t:17:2}"

Bash, ksh, zsh etc have the ${var:n:m} substring expansion, and if you need to do it recursively, enable globstar and use **/IMG_*.jpg instead.

Doing it with find needs the shell, too:

$ find -name "IMG_*.jpg" -exec bash -c '
  for f; do t=${f##*/}; touch "$f" -t "${t:4:8}${t:13:4}.${t:17:2}" ; done
  ' sh {} \+

The first argument to Bash after the script goes to the $0 variable, which usually contains the shell name. The rest go to the positional parameters $1, $2, etc. and for f or for f in "$@" loops over them. The construct is here is rather common (at least in unix.SE answers). The main part of the loop here is the same as in the first example.

The find command in your question find .. -exec touch -t"$(...)" {} \; doesn't work since the command substitution is in double quotes, so it gets expanded before find runs. The {} is piped literally to awk, and it just so happens that the awk script only prints a dot.

If you did put the command substitution in single quotes, it would be passed to touch as-is (find would only substitute {}). touch would get the argument -t$(echo ...), dollar signs, parenthesis and all. find doesn't run the command through the shell by itself, we need to explicitly do it, as above.

In single quotes example:

$ find -name x -exec echo 'hello $(foo {})' \;
hello $(foo ./x)
  • Works great, but I still don't know what's wrong with my approach. Why "$()" returns . instead formatted date?
    – mx0
    Dec 8, 2017 at 15:50
  • @mx0, ah good point, perhaps that should have been elaborated. edited.
    – ilkkachu
    Dec 8, 2017 at 16:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .