Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have a folder named /home/user/temps which has 487 folders. In each folder I have a file called thumb.png.

I want to copy all files named thumb.png to a separate folder and rename them based on the folder they came from.

share|improve this question

migrated from stackoverflow.com Apr 20 '11 at 13:43

This question came from our site for professional and enthusiast programmers.

    
rename them, how? If you replace the directory delimiter with something - let's say underline (_), you may get collisions with files, which already contain an underline. That's true for every valid character, and beside / and \0, which are forbidden in filenames, there is no safe harbor - any character might produce a collision. –  user unknown Apr 20 '11 at 16:46
    
Do folders have subfolders, or just a plain, wide folderstructure? –  user unknown Apr 20 '11 at 17:06
add comment

5 Answers 5

up vote 6 down vote accepted

Here you go:

for file in /home/user/temps/*/thumb.png; do new_file=${file/temps/new_folder}; cp "$file" "${new_file/\/thumb/}"; done;

edit:

the canonical wisdom, by the way, is that using find for this is a bad idea -- simply using shell expansion is much more reliable. Also, this assumes bash, but I figure that's a safe assumption :)

edit 2:

for clarity, I'll break it down:

# shell-expansion to loop specified files
for file in /home/user/temps/*/thumb.png; do

    # replace 'temps' with 'new_folder' in the path
    # '/home/temps/abc/thumb.png' becomes '/home/new_folder/abc/thumb.png'
    new_file=${file/temps/new_folder};

    # drop '/thumb' from the path
    # '/home/new_folder/abc/thumb.png' becomes '/home/new_folder/abc.png'
    cp "$file" "${new_file/\/thumb/}";
done;

details on the ${var/Pattern/Replacement} construct can be found here.

the quotes in the cp line are important to handle spaces and newlines etc. in filenames.

share|improve this answer
    
new_file=new_folder? What's the purpose of the backslash in front of the slash in new_file/\/thumb? –  user unknown Apr 20 '11 at 17:07
1  
@user: ${VARIABLE/PATTTERN/REPLACEMENT} produces the value of VARIABLE but with the pattern replaces by the replacement text. Here the pattern is /thumb (the / needs to be escaped so that it doesn't look like ${new_file//PATTERN/REPLACEMENT}, which makes it a global replacement instead of a first-occurrence replacement) and the replacement text is empty. –  Gilles Apr 20 '11 at 20:43
    
Of course, now I see it. :) –  user unknown Apr 20 '11 at 21:11
1  
You're assuming that the 487 directories are directly under /home/user/temps, which is not clear from the question. If they aren't, you need find or **. –  Gilles Apr 21 '11 at 7:00
    
@Gilles -- I am indeed, that's how I read the question. Appreciate the point, though :) –  simon Apr 21 '11 at 8:18
add comment

Short helper code:

#!/bin/bash
#
# echo cp "$1" ../tmp/"${1//\//_}" 
#
mv "$1" ../tmp/"${1//\//_}"

let's name it 'deslash.sh' and make it executable. Call it with:

find -type f -name thumb.png -exec ./deslash.sh {} ";"    

It will fail, if a collision exists

a/b/thumb.png # and 
a_b/thumb.png 

but that's unavoidable.

share|improve this answer
add comment

This works for arbitrarily deep subdirectories:

$ find temps/ -name "thumb.png" | while IFS= read -r NAME; do cp -v "$NAME" "separate/${NAME//\//_}"; done
`temps/thumb.png' -> `separate/temps_thumb.png'
`temps/dir3/thumb.png' -> `separate/temps_dir3_thumb.png'
`temps/dir3/dir31/thumb.png' -> `separate/temps_dir3_dir31_thumb.png'
`temps/dir3/dir32/thumb.png' -> `separate/temps_dir3_dir32_thumb.png'
`temps/dir1/thumb.png' -> `separate/temps_dir1_thumb.png'
`temps/dir2/thumb.png' -> `separate/temps_dir2_thumb.png'
`temps/dir2/dir21/thumb.png' -> `separate/temps_dir2_dir21_thumb.png'

The interessting part is the parameter expansion ${NAME//\//_}. It takes the content of NAME, and replaces every occurence of / with _.

Note: the result is dependent on the working directory and the path parameter for find. I executed the command from the parent directory of temps. Replace cp with echo to experiment.

share|improve this answer
    
Fails on files which contain a newline in their name. –  user unknown Apr 20 '11 at 21:29
    
Well that worked very well . one thing more , i have few zip files in some sub directories as well . is it possible to extract imgae from there as well . remember 'zip not tar' –  Mirror51 Apr 21 '11 at 13:45
add comment

To copy you need the command cp, and to rename for linux is the same than moving the file, so you have to do it with mv command. In Linux you always have to specify the whole path, from the source, if you are in another folder, and to the destination folder, of course. I'd be something like this, for copy:

cp /source_path/file /destination_path/

or to rename or move

mv /source_path/old_file /destination_path/new_name_file
share|improve this answer
    
This is only a minor part of the poster's question. Now explain how to iterate over the directories and copy the thumbnails :-) –  DarkDust Apr 20 '11 at 13:38
    
the OP wants to use find –  pavium Apr 20 '11 at 13:38
    
Yes, you're right... I don't know what happened to me... –  elvenbyte Apr 20 '11 at 13:40
add comment

Try this

mkdir /home/user/thumbs
targDir=/home/user/thumbs

cd /home/user/temps

find . -type d | 
 while IFS="" read -r dir ; do
   if [[ -f "${dir}"/thumb.png ]] ; then
     echo mv -i "${dir}/thumb.png" "${targDir}/${dir}_thumb.png"
   fi
done

Edit

I have added quoting in case any of your dir names have white-space chars embedded in them.

Also I have changed this so it will only print out the commands to be executed. Examine the output of the script to be sure all files/path names look proper. When you're sure there are no issues with the commands that will be executed, remove the echo.

share|improve this answer
    
I have some png files in some more subfolders , will that work –  Mirror51 Apr 20 '11 at 14:43
    
for my "A A"-folder, it produces: mv -i ./A A/thumb.png ../tmp/.png - all files end in ../tmp/.png (../tmp is my target dir). –  user unknown Apr 20 '11 at 17:14
    
Add quotes and curly braces around all variables in above code, i.e. "${dir}" –  shellter Apr 20 '11 at 18:45
    
@Mirror51 wrote : " files in some more subfolders , will that work ". Yes it should. Did you know you can execute each step of this script separately (or when piped together, each set of pipes). You can execute just the find -type d and see the output. Does that get all the directories you expect? It should, if not, please add some output to show the problem. Good Luck. –  shellter Apr 20 '11 at 20:33
    
Won't work. read dir performs word splitting, and takes, from a directory a\nb a and b as two instances, which no masking can solve. –  user unknown Apr 20 '11 at 21:42
show 3 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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