Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Using bash, how I can batch rename a large number of files, where I need to construct a new name for each file, based on its existing name and a 'contextual map' - don't know what else to call it – of name fragments.

I have a lot of files in like this:


and a map like this:


And now I want to rename all files, so that 001.jpg becomes 001-white.jpg etc..

How can I do this?

share|improve this question

bash4: First read the mappings into an array

declare -A arr2
while IFS=, read -r -a arr; do
done < mapfile.txt

And then rename

for f in [0-9][0-9][0-9].jpg; do
    if [[ "${arr2[${f%.*}]+_}" ]]
        mv -- "${f}" "${f%.*}"-"${arr2[${f%.*}]}"."${f##*.}";
        mv -- "${f}" "${f%.*}"-default."${f##*.}";
share|improve this answer
Will work in zsh but not in bash or ksh93 where numbers with a leading 0 are considered as octal. – Stéphane Chazelas Nov 9 '13 at 20:15
@StephaneChazelas, i tested this on GNU bash, version 4.2.25 before posting, it works. I am guessing this is because arr2 is considered an associative array by default under bash 4 so the index is not considered an "arithmetic context"? – iruvar Nov 10 '13 at 16:15
If a file isn't in the map, it's renamed to e.g. 042-.jpg. You may want to add a test for this. – Gilles Nov 10 '13 at 19:41
@1_CR, have you tested with 008 in the mapfile? – Stéphane Chazelas Nov 10 '13 at 20:14
@Gilles, thanks, added – iruvar Nov 10 '13 at 20:55

You can simply execute

join -t. -j1 <(tr , . < map) filelist | sed 's/\./-/' | paste filelist - | xargs -L1 mv

where map is the contextual map and filelist is the list of files. The join will split on . and uses the first field as a join value; both lists must be sorted. paste will then glue each line of filelist with the newly generated filename together in one line, while xargs then calls mv on each of those lines.

share|improve this answer

Here you go:

for file in [0-9][0-9][0-9].jpg;do
    name="${file%%.*}" # Remove extension
    map="$(grep "$name" map_file | cut -d',' -f 2)"
    echo mv "$file" "$name-$map".jpg

This assumes the "map" is in the file map_file. In recent versions of Bash, you can also transform this file into an associative array and use that for convenience (instead of going through the map_file for each new file you want to rename).

By the way, this will only echo the mv commands. Remove the echo when you feel that everything is in order to actually do the renaming.

share|improve this answer
< map sed 's/\([^,]*\),\(.*\)/mv -- "\1.jpg" "\1-\2.jpg"/' | sh -x

(that assumes the map file doesn't contain ", $, backslash or backtick characters.

share|improve this answer
this looks interesting, very terse, can you explain how this works? – mikkelbreum Nov 10 '13 at 0:09
If the map mentions a non-existent file, you'll get an error message, which is likely not desired. – Gilles Nov 10 '13 at 19:42

Your Answer


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.