Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I have a load of directories (2005 - 2012), each with files (01.jpg - 100.jpg).

If I wanted to rename all the files into the base directory, renamed to, for example, Folder 2005 - 01.jpg, what would be the easiest way of doing this by the command line in Linux?

For example from

/home/mark/images/2005/01.jpg
/home/mark/images/2005/02.jpg
/home/mark/images/2005/03.jpg
/home/mark/images/2006/01.jpg
/home/mark/images/2006/02.jpg
/home/mark/images/2006/03.jpg

to

/home/mark/images/Folder 2005 - 01.jpg
/home/mark/images/Folder 2005 - 02.jpg
/home/mark/images/Folder 2005 - 03.jpg
/home/mark/images/Folder 2006 - 01.jpg
/home/mark/images/Folder 2006 - 02.jpg
/home/mark/images/Folder 2006 - 03.jpg

Surely there must be a simple one liner for this? I know that you can use, e.g. {2005-2012} to access the multiple directories, but I'm not sure how to then access that value later when renaming.

share|improve this question

3 Answers 3

up vote 2 down vote accepted
#!/bin/bash
for year in 20??; do
  pushd "$year"
  for file in *; do
    echo mv "$file" ../"Folder ${year} - ${file}"
  done
  popd
done

Remove the echo if the output looks good to you.

share|improve this answer
1  
The pushd and popd are nice. Saves thinking and one line of code in this very simple example. I didn’t know them. –  erik Feb 12 '13 at 1:22
1  
@erik: Just a note: one can also use the regular cd directory and then cd - to return to the previous directory. This is defined in POSIX which makes it de facto standard across shells, unlike pushd/popd. The latter method supports (where it is supported) a stack of directories, though, if one wants to hold more than the most recent directory in "memory". –  Daniel Andersson Feb 12 '13 at 12:29
    
Thanks! This would work nicely and seems like a nice, quick, simple way. I had hoped there was a nice build in regex-y way, but I guess there is no need to over complicate things! Also, I didn't know about the pushd/popd, although I would agree that if cd - is standard, that seems like a good way to go.. Anyway, thanks! –  Mark Feb 15 '13 at 12:07

Try this one assuming you are in the directory with the 2005 to 2012 directories):

start=$PWD
for directory in *; do
  cd "$directory"
  for filename in *; do
    mv "$filename" ../"Folder $directory - $filename"
  done
  cd "$start"
done

Or as a oneliner

start=$PWD; for directory in *; do cd "$directory"; for filename in *; do mv "$filename" ../"Folder $directory - $filename"; done; cd "$start"; done

Tree of input:

.
├── 2005
│   ├── 01.jpg
│   ├── 02.jpg
│   └── 03.jpg
├── 2006
│   ├── 01.jpg
│   ├── 02.jpg
│   └── 03.jpg

Tree of output:

.
├── 2005
├── 2006
├── Folder\ 2005\ -\ 01.jpg
├── Folder\ 2005\ -\ 02.jpg
├── Folder\ 2005\ -\ 03.jpg
├── Folder\ 2006\ -\ 01.jpg
├── Folder\ 2006\ -\ 02.jpg
└── Folder\ 2006\ -\ 03.jpg
share|improve this answer
    
+1 for the verification via tree outputs –  Sudipta Chatterjee Feb 12 '13 at 1:25
    
Thanks for the answer. Only picked the other as it was posted first, and since they are so similar, went on that basis.. but thank you! –  Mark Feb 15 '13 at 12:07

You probably have Perl and therefore in most distributions also the prename command installed (often linked to simply rename, in e.g. Debian and Ubuntu).

If that is the case, then you can do it with a one-liner:

$ ls *
2005:
01.jpg  02.jpg  03.jpg

2006:
01.jpg  02.jpg  03.jpg
$ rename 's#(.+)/(.+)#Folder $1 - $2#' */*
$ ls -F1R
.:
2005/
2006/
Folder 2005 - 01.jpg
Folder 2005 - 02.jpg
Folder 2005 - 03.jpg
Folder 2006 - 01.jpg
Folder 2006 - 02.jpg
Folder 2006 - 03.jpg

./2005:

./2006:

The rename command uses a regular expression that

  1. catches the file name up to the last directory separator / (e.g. 2005) and stores it in $1
  2. catches the file name after last directory separator / (e.g. 01.jpg) and stores it in $2
  3. renames each file to Folder $1 - $2 (e.g. Folder 2005 - 01.jpg).

Use the rename -n switch to see what changes will be made before running the command for real.


Note: This pattern:

s#(.*?)([^/]+)/([^/]+)$#$1Folder $2 - $3#

would be more robust since it will allow you to rename the files correctly even if you don't give the relative path notation from the specific directory in the example. This pattern is a bit more convoluted to explain, though, since it relies on more complex matching properties.

share|improve this answer
    
Thanks! Nice alternative. I picked the other answer because it will be easier for me to remember, and it is guaranteed to be built into every shell. But this does look like the kind of thing I was looking for - I just assumed there was something built in without having to resort to perl.. But very interesting to analyse the regex. Nice elegant solution. I definitely need to learn perl! :) –  Mark Feb 15 '13 at 12:09

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.