1

I have files in the format YYYY_MM_DD_HH:MM:SS.swf that dump into a folder /home/user/dump/

I want to move these files into a new directory tree /home/user/save/year/month/day/ based on the YYYY_MM_DD from the filename. Alternatively, if these can be modified by the file modification date, that is acceptable as well. I've found a couple other scripts on here, but they don't seem to have all the info I'm looking for.

don_crissti
  • 79,330
  • 30
  • 216
  • 245
RickA
  • 33
  • 1
  • 5
  • what did you do so far ? What is failing ? – MelBurslan Feb 29 '16 at 21:33
  • I've tried [this](http://unix.stackexchange.com/questions/261888/help-creating-script-to-move-files-based-on-date-or-filename) script on the first response, but it spits out an error on line 21. I'm not sure if it would output the /year/month/day/ structure. – RickA Feb 29 '16 at 21:38
  • All files hold this same pattern. No, no directories should be ignored. Hopefully the format is something like 2016/02/29 – RickA Feb 29 '16 at 23:15

4 Answers4

3

If you have the Perl-based rename (sometimes known as prename) you can do this with a single command:

cd /home/user/dump
rename -v 'use File::Path qw(make_path); m!^((....)_(..)_(..)_(.*))!; my $d = "$2/$3/$4"; make_path($d); s!^!$d/!' *

Actually this is a fairly ugly (mis)use of rename. For each file, the code runs as follows

  1. Include a system library that allows for directory path creation
  2. Match against the YYYY_MM_DD structure at the beginning of the filename
  3. Create the corresponding directory path YYYY/MM/DD (if necessary)
  4. Move (rename) the file into its YYYY/MM/DD directory, leaving its name unchanged
roaima
  • 107,089
  • 14
  • 139
  • 261
2

Zsh

Use the zmv function to move or rename files matched by a wildcard expression. There's no built-in way to create the destination directory, so I provide a function to do it.

autoload -U zmv
mkdir_mv () {
  mkdir -p -- ${(P)#:h}
  mv -- $@
}
cd /home/user/dump
zmv -p mkdir_mv '(????)_(??)_(??)_??:??:??.swf' '/home/user/save/$1/$2/$3/$f'

POSIX shell

If you need a portable solution, use a shell loop over the files, and shell string manipulation to extract the parts of the file names.

cd /home/user/dump
for f in ????_??_??_??:??:??.swf; do
  year=${f%%_*}; suffix=${f#*_}
  month=${suffix%%_*}; suffix=${suffix#*_}
  day=${suffix%%_*}
  mkdir -p "/home/user/save/$year/$month/$day"
  mv "$f" "/home/user/save/$year/$month/$day/$f"
done
Stephen Kitt
  • 411,918
  • 54
  • 1,065
  • 1,164
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
0
while read file
 do 
     f=$(basename $file)
     year=$(echo "$f"|cut -f1 -d_)
     day=$(echo "$f"|cut -f3 -d_)
     month=$(echo "$f"|cut -f2 -d_)
     new_dir="/home/user/save/$year/$month/$day"
     mkdir -p "$new_dir"
     mv "$file" "$new_dir"
done < <(find /home/user/dump -type f -name "*_*_*_*:*:*.swf")
Gregg Leventhal
  • 7,480
  • 19
  • 65
  • 100
  • I ran this script, and it created /home/user/dump/home/user/save/2016/02/file.swf -- it is missing the day folder. I might be missing something here but this is close – RickA Feb 29 '16 at 23:21
  • Perhaps you're wrong about the file format? Because this should just work. – Gregg Leventhal Feb 29 '16 at 23:25
  • Show me the output of ls where the files live perhaps? – Gregg Leventhal Feb 29 '16 at 23:26
  • This is actually very close. 'for file in $(find /home/user/dump/ -name "*.swf") do year=$(echo ${file}|cut -d_ -f1) month=$(echo ${file}|cut -d_ -f2) day=$(echo ${file}|cut -d_ -f3) if [ ! -d /home/user/dump_save/${year}/${month}/${day} ] then mkdir -p /home/user/dump_save/${year}/${month}/${day} fi mv ${file} /home/user/dump_save/${year}/${month}/${day} done ' This duplicates the directory, though and creates 'user/dump_save/home/user/dump/2016/02/day' where the day is working correctly. ls of where the files lives /home/user/dump/2016_02_26_06:51:40.swf – RickA Feb 29 '16 at 23:33
  • It shouldn't, that isn't what the code says to do, are you editing the code? How are you running it? Both answers given to you were correct as far as I can see, so my instinct is that you are leaving out info, or doing something incorrectly. – Gregg Leventhal Feb 29 '16 at 23:35
  • Try my code again, I see where there may have been a problem. It should work now. – Gregg Leventhal Feb 29 '16 at 23:40
  • That aside, the whole `while..read` and `echo | cut` three times is - just like Mel's answer - a terrible approach to solve this... – don_crissti Feb 29 '16 at 23:42
  • Why is it terrible? – Gregg Leventhal Feb 29 '16 at 23:44
  • The parts edited are the new_dir address and find address to match my directory. For some reason it creates a new tree /home/user/save/home/user/2016/02/29/, but otherwise it is working. EDIT: whatever edit you changed in the source script, it is now working. – RickA Feb 29 '16 at 23:44
  • don_crissti, is there a more elegant solution? – RickA Feb 29 '16 at 23:45
  • It's a fine approach, he's just being hypercritical. This is a one liner, an elegant solution would be an actual script. – Gregg Leventhal Feb 29 '16 at 23:46
  • It sure is a lot more than I could create. Thanks Gregg. Also, for posterity, can you tell me what you changed from the original script? – RickA Feb 29 '16 at 23:47
  • I did have an error, I was naming the new directories based on the full path of the old file instead of the file name. I added a variable $f which points to the basename of the file to fix that. – Gregg Leventhal Feb 29 '16 at 23:53
  • The other guy Mel made the same kind of error. Find will return an absolute path if you give it one, we both neglected to consider that. – Gregg Leventhal Feb 29 '16 at 23:56
  • Note that this would choke on file names containing spaces and other special characters. Also using pipes to `cut` to process strings is rather slow. – Gilles 'SO- stop being evil' Dec 26 '16 at 23:47
0
for file in $(find /home/user/dump/ -name "*.swf")
do
  year=$(echo ${file}|cut -d_ -f1)
  month=$(echo ${file}|cut -d_ -f2)
  day=$(echo ${file}|cut -d_ -f3)
  if [ ! -d /home/user/save/${year}/${month}/${day} ]
  then
    mkdir -p /home/user/save/${year}/${month}/${day}
  fi
  mv ${file} /home/user/save/${year}/${month}/${day}
done
derobert
  • 107,579
  • 20
  • 231
  • 279
MelBurslan
  • 6,836
  • 2
  • 24
  • 35
  • This is close - it moves the files, but 'doubles' the directory tree, i.e. saves the files in /home/user/save/home/user/save/year/month. Also, it omits the 'Day' folder – RickA Feb 29 '16 at 23:06
  • Then your file name format is not what you presented it to be, i.e. `YYYY_MM_DD_HH:MM:SS.swf`. Please post a section of the output from `ls -l /home/user/dump/" – MelBurslan Feb 29 '16 at 23:18
  • File format example '"/home/user/dump/2016_02_26_06:51:40.swf"' or '2016_02_27_09:57:14.swf' – RickA Feb 29 '16 at 23:27
  • Note that this would choke on file names containing spaces and other special characters. Also using pipes to `cut` to process strings is rather slow. – Gilles 'SO- stop being evil' Dec 26 '16 at 23:46