0

I have few folders like this with their contents

data/
-2011/
---images/
------logo.jpg
------banner.jpg
---reports/
------monthly.pdf

-2012/
---images/
------logo.jpg
---reports/
------monthly.pdf
------yearly.pdf

-2013/
---images/
---reports/
------yearly.pdf

-2014/
---images/
------logo.jpg
---reports/
------yearly.pdf

I want to merge and sort all files prioritizing newer files than older, while using the older versions if no newer ones exists, and deletes the year folder.

I'm trying to get this results, preferred to be a one-liner

data
---images/
------logo.jpg      (2014 version)
------banner.jpg    (2011 version)
---reports/
------monthly.pdf   (2012 version)
------yearly.pdf    (2014 version)

EDIT:

Thanks Jeff, that worked exactly how i wanted, however how would i do the same with slight difference in the parent folder names, prioritizing folders as numeric instead date?

something like

$ ls -1v

0-6681c0254979c686962dc9
1-d2ed427d79143b923bfe
2-c52049453e80f3a1e9b6
3-b2d885e0115202d0e36d858c9b7d
4-d6d9bb862794c08d325f
5-009cbde4ddbbd23a3da1
6-c35ed13e6aa9ed066330b
7-2f46a484da2cfeab616afb
8-e23a8f406ca869b4dfeb98
9-5150fa1b9721ec7
10-4ba9db442af3ac9163bd4
11-a4dd9a37f613bb0bbb1c5
12-996d279d3bb6b8c73b2416

instead of

$ ls -1v

2011
2012
2013
2014
  • Just curious why you want to subject your future self (or your replacement) to a one-liner? – Jeff Schaller Jun 19 at 18:46
  • One (simple) approach would just loop through the directories in natural order and copy every file to its destination. Newer files would overwrite older files. Simple to understand, if you don't care about I/O efficiency? – Jeff Schaller Jun 19 at 19:04
  • I’ll use it as a step in a small script I’m working on, and I prefer one-liners if it does it’s job – Assemator Jun 19 at 19:05
  • I don’t care about I/O as long it does what I want and the newer versions are prioritized by the year folder names – Assemator Jun 19 at 19:07
1

Here's a simple version, if you don't care about brute-force copying every file. It loops over the directories (assuming four-digit years, as in your example), naturally sorting them in numerical order, and copying their contents to the current directory:

for date in [[:digit:]][[:digit:]][[:digit:]][[:digit:]]/; do cp -r "$date"/* .; done

Broken out to multiple lines:

for date in [[:digit:]][[:digit:]][[:digit:]][[:digit:]]/
do 
  cp -r "$date"/* .
done

If you have dotfiles in the top-level directories (directly under the years), then you would want to shopt -s dotglob ahead of time.

This copies every file, allowing "newer" (files from later years) files to clobber older files.


If your directory names are different, then you need a different wildcard/globbing pattern. In the case of numeric prefixes that aren't zero-padded, I would suggest:

for directory in ?-* ??-* ???-* ... etc ...

as many as you need to capture the highest numeric prefix. The sorting is done in two places:

  1. location-based: the single-digit directory names are listed first in the for loop, the two-digit names second, etc...
  2. within each wildcard pattern, the directory names are sorted lexically, so 10-etc will come before 11-etc, and so on.
  • Thank you Jeff, your answer did exactly what i wanted, but check my question again, i edited it to give you better explanation of the problem. – Assemator Jun 19 at 20:08
  • See the edit for dealing with the different directory names... – Jeff Schaller Jun 20 at 13:07
  • For more fine-grained control over the sorting, consider zsh: for dir in *(/oe_'REPLY=${REPLY%%-*}'_n) ... – Jeff Schaller Jun 20 at 13:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.