LinuxQuestions.org
Download your favorite Linux distribution at LQ ISO.
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie
User Name
Password
Linux - Newbie This Linux forum is for members that are new to Linux.
Just starting out and have a question? If it is not in the man pages or the how-to's this is the place!

Notices

Reply
 
Search this Thread
Old 02-25-2011, 10:32 PM   #1
MikeG001
LQ Newbie
 
Registered: Feb 2011
Posts: 4

Rep: Reputation: 0
Script to rename files


Please bear with me... I have some linux experience. But I'm stumped.

I have 500+ directories with a single file in each directory.
I would like to rename each file in the directory with the directory name and move the file to another directory.

I can do this manually, but there must be a script to to this.

System Details: Fedora 14

Any help?
 
Old 02-25-2011, 11:51 PM   #2
jbiggs12
LQ Newbie
 
Registered: Feb 2011
Location: /dev/null
Distribution: Gentoo
Posts: 14

Rep: Reputation: Disabled
You should be able to set up a simple bash script by using the ls command while you're in the directory containing the folders, and then cd into each dir and copy the file back. Try looking at some bash tutorials. ls will list the files and directories in a specific directory, and mv will move and/or rename files. For example, if I wanted to see all of the files in my home directory, I would type:
Code:
ls /home/USER
, and to move (and rename) a file I would type
Code:
mv /home/USER/name/name /home/USER/name
.

Sorry if I'm not that much help to you, I don't program bash but it should be a fairly easy first project
 
Old 02-26-2011, 12:20 AM   #3
Dark_Helmet
Senior Member
 
Registered: Jan 2003
Posts: 2,786

Rep: Reputation: 369Reputation: 369Reputation: 369Reputation: 369
Perhaps I can help, but you need to flesh out the problem a little more.

How are the directories and files organized? Is there a single directory that all of them are under? For instance:
/home/usera/myfiles
/home/usera/myfiles/dir01/filename
/home/usera/myfiles/dir02/filename
/home/usera/myfiles/dir03/filename
...

Do the filenames follow any sort of pattern? Do they all have the same name--just differentiated by the parent directory?

When you say you want the directory added to the filename, which part of the directory? Just the parent directory? The full, absolute path? Since you can't include a '/' as part of a filename, what character would you like to use to separate the path component(s) from the filename?

In a nutshell, it would probably be easiest to give an example of what your set up looks like as it stands now, and then show what you would like it to be after the script runs.
 
Old 02-26-2011, 01:02 PM   #4
MikeG001
LQ Newbie
 
Registered: Feb 2011
Posts: 4

Original Poster
Rep: Reputation: 0
jbigs12/Dark_Helmet thanx for the response,

The directory structure is as follows.

/home/usera/myfiles
/home/usera/myfiles/Kathy Johnson/image123.jpg
/home/usera/myfiles/Fred Smith/image324.jpg
/home/usera/myfiles/Bill Smith/image 657.jpg

I would like to have.

/home/usera/myfiles/new_Directory/Kathy_Johnson.jpg (For /home/usera/myfiles/Kathy Johnson/image123.jpg)
/home/usera/myfiles/new_Directory/Fred_Smith.jpg (For /home/usera/myfiles/Fred_Smith/image324.jpg)

The files are coming from a windows system hence the spaces. The only pattern is ..../Name/filename.jpg
I have the ability to relocate the complete structure to a single drive(s) if that makes the script easier.

The assistance is appreciated.
 
Old 02-26-2011, 02:15 PM   #5
Dark_Helmet
Senior Member
 
Registered: Jan 2003
Posts: 2,786

Rep: Reputation: 369Reputation: 369Reputation: 369Reputation: 369
Here is a possible solution based on that info.

Code:
#!/bin/bash

SOURCE_DIR=/home/usera/myfiles
DEST_DIR=/home/usera/myfiles/new_Directory

find ${SOURCE_DIR} -type f -iname "*.jpg" -print0 | while read -d $'\0' filename ; do
  removeSpaces=$( echo "${filename}" | sed 's@ @_@g' )
  parentDirectory=${removeSpaces%/*}
  newFilename="${DEST_DIR}/${parentDirectory##*/}.jpg"

  echo mv "\"${filename}\"" "\"${newFilename}\""
#  mv "${filename}" "${newFilename}"
done
Please note: this script will do nothing until the hash mark is removed from the mv command. Do not remove it until you are certain that the script will do what you expect. Even then, it's probably a good idea to run against a test data set. Or you might consider changing the mv to a cp--so that there is no risk of data loss.

You need to:
(1) modify the values assigned to SOURCE_DIR and DEST_DIR to match your setup
(2) make sure that DEST_DIR actually exists (i.e. use mkdir to create it if necessary)
(3) make at least one dry-run and make sure the echo statements are "sane"

This script will search for any file with a "jpg" extension beneath SOURCE_DIR. For any file found, the script will determine the new filename by (1) substituting underscores for spaces; (2) determining the immediate parent directory; and (3) adding a "jpg" extension to the parent directory name.

Things that would cause the train to go off the tracks: multiple jpg files in the same directory or multiple subdirectories that share the same name (e.g. SOURCE_DIR/Kathy and SOURCE_DIR/somesub/Kathy).

In the case of multiple files in one directory, subsequent jpg files would overwrite previous jpg files (because all files would share a common parent directory, and hence a single destination filename).

Similarly, if multiple directories share the same name and each has a jpg file, the script would generate the same filename for each file and subsequent files would overwrite previous ones.

Lastly, the script will not remove the directories that are emptied after the script runs. You'll need to do that manually if there is no other purpose for them.

Last edited by Dark_Helmet; 02-26-2011 at 02:19 PM.
 
Old 02-26-2011, 02:18 PM   #6
mmhs
Member
 
Registered: Oct 2010
Posts: 99

Rep: Reputation: 1
copy this script to /home/usera/myfiles and run it (
Code:
#!/bin/bash
mkdir ./new_Directory
for file in $(find . -name *.jpg)
do 
m=$(basename $file)
f=$(echo $file|awk -F/ '{print $2}')
echo $f
echo $m
mv ./$f/$m ./$f/$f.jpg
cp ./$f/$f.jpg ./$f/new_Directory
done
 
Old 02-26-2011, 02:35 PM   #7
Dark_Helmet
Senior Member
 
Registered: Jan 2003
Posts: 2,786

Rep: Reputation: 369Reputation: 369Reputation: 369Reputation: 369
Be cautious: mmhs's script does not handle filenames with spaces in them. Given that the subdrectories and some of the filenames themselves have spaces, that is a serious concern.
 
Old 02-26-2011, 06:52 PM   #8
kurumi
Member
 
Registered: Apr 2010
Posts: 223

Rep: Reputation: 45
Ruby(1.9+)

Code:
#!/usr/bin/env ruby  
newdir="/home/usera/myfiles"
Dir.mkdir(newdir) if not Dir.exists?(newdir)
Dir["/home/usera/myfiles/**/im*jpg"].each do |file|
  filename = File.dirname(file).split("/")[-1].gsub(/\s+/,"_")+".jpg"
  directory = [newdir,filename]
  File.rename( file , directory.join("/") )
end
 
Old 02-26-2011, 09:06 PM   #9
MikeG001
LQ Newbie
 
Registered: Feb 2011
Posts: 4

Original Poster
Rep: Reputation: 0
Dark_Helmet, thanx.

Let me understand (my learning) what you have done.
============
#!/bin/bash

SOURCE_DIR=/home/usera/myfiles
DEST_DIR=/home/usera/myfiles/new_Directory

find ${SOURCE_DIR} -type f -iname "*.jpg" -print0 | while read -d $'\0' filename ; do
removeSpaces=$( echo "${filename}" | sed 's@ @_@g' )
parentDirectory=${removeSpaces%/*}
newFilename="${DEST_DIR}/${parentDirectory##*/}.jpg"

echo mv "\"${filename}\"" "\"${newFilename}\""
# mv "${filename}" "${newFilename}"
done
===========
1) Find all files (-type f) with pattern *.jpg (-iname "*.jpg") (Used " to ensure you have full file name including spaces?) then print to Stdout
2) Piped that to a while loop (while ; do ..... done)
3) In the while loop read until no file name(read -d $'\0') ('\0' Null character?)
4) Take filename and put into variable filename
5) Convert spaces to _ using sed and put into variable removeSpaces
- why the echo?
6) create variable parentDirectory
- lost me here, does the %/* just capture the directory name.
7) create variable newFilename
- Think I understand this line
7) Then why the echo? Just to see output? == echo mv "\"${filename}\"" "\"${newFilename}\"" ==
8) Actual move command


Do have this right?

There is no way I could have done this. Thank you very much.
Sorry to ask again, but I like to learn

mike g.
 
Old 02-26-2011, 10:38 PM   #10
Dark_Helmet
Senior Member
 
Registered: Jan 2003
Posts: 2,786

Rep: Reputation: 369Reputation: 369Reputation: 369Reputation: 369
Quote:
1) Find all files (-type f) with pattern *.jpg (-iname "*.jpg") (Used " to ensure you have full file name including spaces?) then print to Stdout
2) Piped that to a while loop (while ; do ..... done)
3) In the while loop read until no file name(read -d $'\0') ('\0' Null character?)
4) Take filename and put into variable filename
Mostly, yes. Just for reference while I explain what I did:
Code:
find ${SOURCE_DIR} -type f -iname "*.jpg" -print0 | while read -d $'\0' filename ; do
...
done
'-type f' -- only match actual files
'-iname "*.jpg"' -- case insensitive match. Find any name that ends with .jpg (e.g. .jpg, .JPG, or any case-combination)

The double quotes around the *.jpg is simply habit. It's born out of frustration dealing with unanticipated/unexpected wildcard expansion. The double quotes just make sure the shell keeps its grubby hands off. The space-handling is done through other means.

'-print0' -- part of the space-handling in the filenames. This tells find to put a NULL character after each matching filename rather than simply a newline. The NULL is used as a marker for the read command.

The results are sent to stdout, and stdout is piped into the read command.

"-d $'\0'" -- causes the read to store everything up to a NULL character into the filename variable. Spaces, tabs, pretty much anything. Whereas without the --print0 and -d options to find and read respectively, the shell loop would separate the find output at any whitespace (space, tabs, newlines, etc.) causing the filename variable to have a piece of the actual path/file.

There are other ways of handling spaces (e.g. modify the IFS shell variable). So if my approach just feels too cryptic, you can probably find another approach that sits better with you.

Quote:
5) Convert spaces to _ using sed and put into variable removeSpaces
- why the echo?
The echo is necessary to give the filename to sed for processing. Sed will process information from a file or from standard input. Since the information that needs to be altered is in a variable, the echo is a pretty simple way of getting the variable's contents to standard output and then piped to sed.

You could do the same thing with parameter expansion (which I'm about to get to), and it would use the same basic syntax as sed. So, I'm guilty of falling into habit (again) for this step.

Quote:
6) create variable parentDirectory
- lost me here, does the %/* just capture the directory name.
Code:
parentDirectory=${removeSpaces%/*}
This is a special form of parameter expansion. So, this tells bash to take the contents of the variable removeSpaces and remove the shortest part of the variable's contents that match the pattern after the '%'. Using the '%' makes the pattern matching start looking from the end of the variable's contents (as opposed to the beginning).

The asterisk is a wildcard that matches anything, and the '/' is a literal slash. removeSpaces contains the full path to the file including the filename. If you remove everything from that full path after (and including) the last slash, you'll be left with the full path to the parent directory--the filename and the last slash will have been stripped.

Quote:
7) create variable newFilename
- Think I understand this line
Code:
newFilename="${DEST_DIR}/${parentDirectory##*/}.jpg"
Ok, another special form of parameter expansion here. Let me tackle that first. Using the double-hash mark (#) means to remove the longest part of the variable's contents that match the pattern after the '##'. Also, using the hash form makes the pattern matching start from the beginning of the variable's contents.

Again, the asterisk is a wildcard that matches anything, and the '/' is a literal slash. So everything in the variable up to and including the last slash is stripped off--which leaves the immediate parent directory name.

The rest of the assignment adds the destination directory to the front and the ".jpg" extension to the end.

You can read more about the special forms of parameter expansion by opening man bash and searching for the parameter expansion section header (e.g. when inside the man page, type '/Parameter Expansion', press Enter, and hit 'n' (if necessary) until you reach the start of the section). You'll see the space-to-underscore style I alluded to before: ${parameter/pattern/string} as well as the discussion of the other forms I used.

Quote:
7) Then why the echo? Just to see output? == echo mv "\"${filename}\"" "\"${newFilename}\"" ==
This was to satisfy you that things are working. This is basically showing what the mv command would look like if it were uncommented.

As a rule of thumb, you should never trust your data to a script or program from the internet until you're sure it will work. There may be no malice involved, but a bug or an unanticipated environment quirk could cause data loss. Getting that data back could be easy (backups) or impossible depending on your situation.

Quote:
8) Actual move command
Yup. And again, remove the '#' when you're comfortable it won't screw anything up.

EDIT: And one last thing--the double quotes around the arguments to the mv command are to guard against the spaces in the source directory and the destination directory (if any).

Last edited by Dark_Helmet; 02-26-2011 at 10:47 PM.
 
Old 02-27-2011, 07:38 AM   #11
MikeG001
LQ Newbie
 
Registered: Feb 2011
Posts: 4

Original Poster
Rep: Reputation: 0
Dark_Helmet, Thank you.

This is slick......
Works like a charm....


mike g.
 
  


Reply

Tags
shell


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Trouble with making a bash script to read in different files and rename output files. rystke Linux - Software 1 05-07-2009 08:00 AM
To rename files in a directory should I use Bash script or a Perl Script ? jamtech Programming 7 01-22-2008 11:25 PM
Rename files with script sharathkv25 Programming 14 06-25-2007 03:00 AM
Script to Rename Many Files geeman2.0 Programming 3 04-05-2006 01:45 PM
Need script to rename files joe_stevensen Programming 5 12-05-2003 06:12 PM


All times are GMT -5. The time now is 03:11 PM.

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration