2

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
...
tom
  • 23
  • I am guessing you need to apply it to a var of some sort, so try new_date=$(date -d @1708532255 +'%Y%m%d_%H%M%S') – Bib Mar 11 '24 at 11:27
  • try remove the @ in front of $filename.. does that help? – Roger Mungo Mar 11 '24 at 11:41
  • removing the @ shows error: date: invalid date '/media/HAVideo/Eingang/Test/2/1709532255' – tom Mar 11 '24 at 11:44
  • 2
    it looks like its treating the whole file path as the filename, hence the 'invalid date' maybe use the basename command to get just the filename – Roger Mungo Mar 11 '24 at 11:48

6 Answers6

5

You just missed a cd:

#!/bin/bash

cd /media/HAVideo/Eingang/Test/2/ || exit # <- what changed for file in *.mp4 do filename=echo &quot;${file}&quot; | cut -d'.' -f1 readable=date -d @$filename +&quot;%Y%m%d%H%M%S.mp4&quot; 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).

4

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.

2

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
cmt
  • 121
  • The only downside I see here is that this appears to move files from /media/HAVideo/Eingang/Test/2/ to the present directory. This can be corrected by putting the directory in front of "$dst", or by using cd to change directory. +1 for using basename, particularly the second parameter (removing the extension). – enkorvaks Mar 11 '24 at 23:11
  • I didn't include that behavior because it wasn't clear from the question that the author intended for the script to behave that way. However, I completely agree with you. In practice keeping all files in the same directory would be the least surprising – cmt Mar 12 '24 at 05:07
  • Thank you for your tips. I now have a version that works well for me. But of course I learn from you with every comment. – tom Mar 12 '24 at 05:27
1

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

0

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.

0

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 \ ` `.