Convert EPOCH string in filename to readable format

My knowledge of Unix is not very good.
I’m trying to write a shell script to rename a set of files in a directory, like 1709532255.mp4 to 20240304_070415.mp4, where 1709532255 is always an epoch time.

I found that

date -d @1708532255 +'%Y%m%d_%H%M%S'

… gives me the wanted format, but I can’t get it to work in the script.

Here my script:

#!/bin/bash
for file in /media/HAVideo/Eingang/Test/2/*.mp4
do
filename=`echo "${file}" | cut -d'.' -f1`
readable=`date -d @$filename +"%Y%m%d%H%M%S"`
mv "${file}" "${readable}"
done

The output is:

...
date: invalid date '@/media/HAVideo/Eingang/Test/2/1709532255'
mv: can't rename '/media/HAVideo/Eingang/Test/2/1709532255.mp4': No such file or directory
...
Asked By: tom

||

Using (usable in any OS).

perl -MTime::Piece -e '
    for (@ARGV) {
        my $file = $_;
        s/..*$//;
        rename $file, Time::Piece->strptime($_, "%s")->strftime("%Y%m%d_%H%M%S")
    }
' *.mp4

Time::Piece‘s module is packed with Perl, no need to install it.

Answered By: Gilles Quénot

NOT TESTED

#!/bin/bash

# Function to convert epoch time to date_time format
epoch_to_datetime () {
  date +%Y%m%d_%H%M%S --date="@$1"
}

# Loop through all files in the current directory
for file in *.mp4; do
  # Extract epoch time from filename (assuming it's the first part)
  epoch_time="${file%%.*}"

  # Convert epoch time to date_time format
  new_filename="$(epoch_to_datetime $epoch_time)"_$(basename "$file")

  # Rename the file using mv command
  mv "$file" "$new_filename"
  echo "Renamed: $file -> $new_filename"
done
Answered By: Roger Mungo

You just missed a cd:

#!/bin/bash

cd /media/HAVideo/Eingang/Test/2/ || exit # <- what changed
for file in *.mp4
do
filename=`echo "${file}" | cut -d'.' -f1`
readable=`date -d @$filename +"%Y%m%d%H%M%S.mp4"`
mv "${file}" "${readable}"
done

But there’s some issues.

Use $( ) instead of backticks. And indent properly.

See http://mywiki.wooledge.org/BashFAQ/082

A better version:

#!/bin/bash -

cd /media/HAVideo/Eingang/Test/2/ || exit
for file in *.mp4 ; do
    mv -- "$file" "$(date -d "@${file%.*}" +"%Y%m%d%H%M%S.mp4")"
done

${file%.*} is a builtin parameter expansion

See: http://mywiki.wooledge.org/BashFAQ/073 and
LESS='+/Parameter Expansion' man bash
Also see wiki.bash-hackers.org/syntax/pe

Since that’s a standard sh operator (from ksh) and there’s no bashism in that code, you could change the shebang to #! /bin/sh - and remove the bash dependency. That date -d is a non-standard GNUism though, which you could avoid by using the printf '%(format)T' of recent versions of bash (copied from ksh93).

Answered By: Gilles Quénot

With zsh:

$ zmodload zsh/datetime
$ autoload zmv
$ cd /media/HAVideo/Eingang/Test/2/
$ zmv -n '(<->)(.mp4)' '$(strftime %Y%m%d_%H%M%S $1)$2'
mv -- 1709532255.mp4 20240304_060415.mp4

(remove the -n (dry-run) if happy).

Beware that while 1709532255 is timezone independent, 20240304_060415 is not and is ambiguous. You can remove the ambiguity and still use local time by using ISO8601-style date format where the offset to UTC is included by replacing %Y%m%d_%H%M%S with %FT%T%z for instance, where I’d get 2024-03-04T06:04:15+0000 and you’d likely get 2024-03-04T07:04:15+0100, same timestamp, but in two different timezones.

Answered By: Stéphane Chazelas

The reason why you are seeing the error:

date: invalid date '@/media/HAVideo/Eingang/Test/2/1709532255'

is because you are passing the full path of the file, minus the extension, due to your cut command.

[...snipped out irrelevant parts...]
filename=`echo "${file}" | cut -d'.' -f1`
...
readable=`date -d @$filename +"%Y%m%d%H%M%S"`

is evaluating to:

readable=`date -d @/media/HAVideo/Eingang/Test/2/1709532255 +"%Y%m%d%H%M%S"`

Therefore, filename is /media/HAVideo/Eingang/Test/2/1709532255.

Your script mostly works. I modified the filename=... line to retrieve only the basename of the file, instead of the full path:

#!/bin/bash
for file in /media/HAVideo/Eingang/Test/2/*.mp4
do
filename=`basename "${file}" | cut -d'.' -f1`
readable=`date -d @$filename +"%Y%m%d%H%M%S"`
mv "${file}" "${readable}"
done

of course, you could go further and remove the unnecessary cut since basename can remove the file extension, and rename the variables so the code is a little clearer:

for src in /media/HAVideo/Eingang/Test/2/*.mp4
do
  epoch="$(basename "$src" .mp4)"
  dst="$(date -d "@$epoch" +"%Y%m%d%H%M%S")"
  mv "$src" "$dst"
done
Answered By: cmt

Some guys offered their solutions, so I focused on 2 things.

  1. To do as less modifications as possible

  2. To get rid of unnecessary failsafe syntax

     for file in /media/HAVideo/Eingang/Test/2/*.mp4 ; do # it is better to do it in one line
       filename=`echo ${file##*/} | cut -d'.' -f1` # we are using globbing here
       readable=`date -d @$filename +"%Y%m%d%H%M%S"` 
       mv $file $readable
     done
    

In real world production there are plenty occasions where you can not get spaces within filenames under any conditions. So I personally advice to get rid of unnecessary quotation in past 2 lines of the cycle.

You can do it if you know exactly what you are doing. Then you will have better, cleaner syntax in your scripts.

Also a word about backticks. I like the advice from the highest ranked answer above. It is usually better to use $() form. Still there’s no big issue with backticks unless you’re starting to make inclusions like ` `.

Answered By: Andrey Shevchenko
Categories: Answers Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.