1

Sorry if the title is unclear. An example folder structure to help understand:

/images/icons/654/323/64/64/icon.png
/images/icons/837/283/64/64/icon.png

to be renamed to

/images-icons-654-323-64-64-icon.png
/images-icons-837-283-64-64-icon.png

I'm not great at bash so all I have to start with is:

find . -name "*.png"

which will find all of the files, which I then am hoping to using -exec rename with, or whatever works. also open to using any language to get the job done!

elzi
  • 5,442
  • 7
  • 37
  • 61

3 Answers3

2

Solution in bash:

for f in $(find images_dir -type f); do 
    mv -v "$f" ${f//\//-}
done

This finds all files in the images_dir directory, replaces any / in their path with - thanks to the parameter expansion, and moves the file to the new path.

For example, file images_dir/icons/654/321/b.png will be moved to images_dir-icons-654-321-a.png.

Note that if you execute find ., you will encounter an issue as find outputs filenames starting with ./, which means your files will be renamed to something like .-<filename>.

As @gniourf_gniourf notices in the comments, this will fail if your file names include spaces or newlines. Whitespace-proof:

find images_dir -type f -exec bash -c 'for i; do mv -v "$i" "${i//\//-}; done' _ {} +
maahl
  • 547
  • 3
  • 17
  • This describes exactly what I want, however it did not work for me. I'm getting `sed: -: No such file or directory` (multiple times) – elzi Feb 10 '16 at 18:06
  • Which command are you running exactly? I had a similar error when i tried to use sed in the wrong way (giving as argument a string instead of a filename). Here, `-` refers to the piped input, so I'm not sure where this error could come from. – maahl Feb 10 '16 at 18:14
  • 1
    Running exactly what you posted in a `script.sh` file. It worked when I removed `-` after the `sed` regex before the `)`. however I changed `images_dir` to `./` and that made things real ugly haha, fortunately had a backup – elzi Feb 10 '16 at 18:17
  • Interesting, I didn't know this hyphen could cause issues! I'll know for later :) – maahl Feb 10 '16 at 18:18
  • And I hadn't thought about `./` indeed :D to fix it you could have simply done `for f in $(find ./ -type f); do mv -v $f $(echo $f | sed 's/.-//' ); done`, which moves all `.-` files to ``. – maahl Feb 10 '16 at 18:23
  • 1
    That's a horrible answer: you're parsing the output of `find`, you lack lots of quotes, you're using an external `sed` while parameter expansions would do the job much better. I'm not going to thank you for spreading bad shell coding over SO. – gniourf_gniourf Feb 10 '16 at 18:25
  • @gniourf_gniourf I'm curious, why is parsing the output of find bad? I've fixed the quotes and learnt about parameter expansion, thanks for that! – maahl Feb 10 '16 at 18:33
  • Bad in case filename contains spaces or glob characters. – gniourf_gniourf Feb 10 '16 at 18:37
  • One possibility: use a `while` loop with `read` and process substitution: `while IFS= read -r f; do ...; done < <(find ...)`. This will still break if filenames contain newlines; to be more robust, you need a version of `find` that handles `-print0` (GNU `find` does) and use: `while IFS= read -r -d '' f; do ...; done < <(find ... -print0)`. (cont'd)… – gniourf_gniourf Feb 10 '16 at 18:40
  • 1
    But the best way to use `find` is to not parse its output at all. E.g., `find images_dir -type f -exec bash -c 'mv -v "$1" "${1//\//-}"' _ {} \;`, or, even better `find images_dir -type f -exec bash -c 'for i; do mv -v "$i" "${i//\//-}; done' _ {} +`. Note that this doesn't fix the slash of `./` being replaced by `-`. – gniourf_gniourf Feb 10 '16 at 18:42
  • 1
    thanks @gniourf_gniourf, I've updated my answer again! – maahl Feb 10 '16 at 19:12
1

In python you could do it like so:

import fnmatch
import os

def find(base_dir, some_string):
    matches = []
    for root, dirnames, filenames in os.walk(base_dir):
        for filename in fnmatch.filter(filenames, some_string):
            matches.append(os.path.join(root, filename))

    return matches

files = find('.', '*.png')
new_files = ['-'.join(ele.split('/')[1:]) for ele in files]

for idx, ele in enumerate(files):
    os.rename(ele, new_files[idx])

And to give proper credit, the find function I took from this answer.

Community
  • 1
  • 1
Lisa
  • 3,365
  • 3
  • 19
  • 30
0

This should do it for you:

for file in `find image -iname "*.png"`
do
  newfile=`echo "$file" | sed 's=/=-=g'`
  mv -v $file $newfile
done

The backtick find command will expand to a list of files ending with ".png", the -iname performs the search in a case independent manner.

The sed command will replace all slashes with dashes, resulting in the new target name.

The mv does the heavy lifting. The -v is optional and will cause a verbose move.

To debug, you can put an echo statement in front of the mv.

Greg Tarsa
  • 1,622
  • 13
  • 18
  • Getting this error: ./script.sh: line 6: syntax error near unexpected token `|' ./script.sh: line 6: ` newfile=`echo "$file" | sed 's=/=-/'`' – elzi Feb 10 '16 at 18:03
  • The backticks make the error unclear due to formatting issues, I've posted it here: https://gist.github.com/elzii/56f3ca4d4f94dc29e955 – elzi Feb 10 '16 at 18:09
  • That's a horrible answer: you're parsing the output of `find`, you lack lots of quotes, you're using an external sed while parameter expansions would do the job much better. I'm not going to thank you for spreading bad shell coding over SO. – gniourf_gniourf Feb 10 '16 at 18:26
  • @gniourf_gniourf: I make no apologies. This is code that should run portably in both bash and sh and uses a few straightforward shell concepts. – Greg Tarsa Feb 11 '16 at 01:53
  • @elzi: I do apologize to you; I fat-fingered a couple of things besides the quotes, in the original answer, but the code is fixed now and should work. – Greg Tarsa Feb 11 '16 at 01:53
  • You mean _broken_ shell _misconceptions,_ don't you? your command is broken with filenames containing spaces or glob characters. Please be aware that parsing `find` has been recognized for a rather long while as being broken and very bad practice. See [ParsingLs](http://mywiki.wooledge.org/ParsingLs) and all the links it contains. – gniourf_gniourf Feb 11 '16 at 07:57