0

I have folders nested various levels deep, with names such as x/y/z/Drawings and a/b/c/x/y/z/Plans. There might be hundreds of upper-level folders that contain a Drawings or Plans sub-folder somewhere in their path.

I then have icons called Drawings.jpg and Plans.jpg. I'd like to copy the correct JPG into all the correct (i.e., correspondingly-named) sub-folders. That is, for every ../Drawings folder, wherever it may be in the folder hierarchy, I'd like to see a ../Drawings/Drawings.jpg, and a ../Plans/Plans.jpg file.

I'm running on Ubuntu, but I cannot think of way of doing a copy of file named Whatever.jpg into every single sub-folder that is named something/something/Whatever. The problem is basically one of single-file source being copied into multiple destinations, based on folder name.

There are likely no more than 15 JPGs that have to be housed appropriately, but there are hundreds of possible destination sub-folders for each JPG.

Is there a series of simple cp commands I can use to achieve this? Or a bit of Bash script that will produce the desired result?

2 Answers 2

0

Since you are happy to consider non-leaf directories as candidates for the copy command, you can base the solution from find.

iconDir=/path/to/icons_directory
rootDir=/path/to/top/of/drawings_projects_etc

find "$rootDir" -type d -exec sh -c '
    iconDir="$0"
    for thisDir in "$@"
    do
        iconName="${thisDir##*/}.jpg"
        [ -f "$iconDir/$iconName" ] && echo cp -p "$iconDir/$iconName" "$thisDir"
    done
' "$iconDir" {} +

What this does is to consider every directory under $rootDir in turn. It looks for an icon file in your $iconDir corresponding to the last part of the directory path (suffixed with .jpg), and if one is found it's copied to the target directory.

As written, you'll get only a statement of intended action. Remove the echo prefix from cp to make it actually copy the files.

I use cp -p rather than cp to retain permissions and - often more usefully - the timestamp of the original file. If your $iconDir and all of your $rootDir tree are all on the same filesystem you might prefer to use ln rather than cp -p.

0

With zsh:

for icon (path/to/icons/*.jpg(N.))
  for dir (path/to/dirs/**/$icon:t:r(N/))
    cp $icon $dir/

To speed it up, there are a number of optimisations you could do:

  • build a list of all dirs first and reuse it so not to have to crawl the path/to/dirs directory tree for each icon
  • make cp builtin to remove the cost of forking a process and executing an external command for each copy
  • do Not order the globs as we don't really need them to be sorted here.
  • disabling multibyte character support as doing byte-to-byte comparisons is enough here.

So

zmodload zsh/files
set +o multibyte
dirs=(path/to/dirs/**/*(N/oN))
for icon (path/to/icons/*.jpg(N.oN))
  for dir (${(M)dirs:#*/$icon:t:r})
    cp $icon $dir/

(of those, what is going to make the most difference is zmodload zsh/files which enables a builtin cp).

You may want to use ln or ln -sr (-r is a GNU extension and not supported by zsh's builtin ln though) instead of cp to make links instead of copies to save space.

You must log in to answer this question.

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