Need help for a Shell script to rename multiple files


 
Thread Tools Search this Thread
Top Forums Shell Programming and Scripting Need help for a Shell script to rename multiple files
# 8  
Old 03-03-2010
Hello, all:

I see several solutions using the following snippet...
Code:
for fn in `ls file*`

... and just wanted to suggest not to do that. After the command substitution, filenames will undergo word splitting. If any of them include whitespace (assuming a default IFS value), the value of fn will not be set correctly for each filename (such filenames will be assigned to fn piecemeal over multiple loop iterations).

Code:
for fn in file*

... will accomplish the task without word splitting issues.

Illustrative example:
Code:
$ touch without_space with\ space

#Incorrect
$ for i in `ls w*`; do echo "$i"; done
with
space
without_space

#Correct
$ for i in w*; do echo "$i"; done
with space
without_space

Regards,
Alister
# 9  
Old 03-03-2010
So sorry -- I was assuming that you knew how to use find.

When I put something in brackets, that is an optional thing. Are all the files in a single directory? If so, you would use "-maxdepth 1" so that find won't go returning all the files contained in the starting directory.

Here is what I am guessing: You a Mac, you used an unerase tool that did not recover the names, and all the files are in a single directory in the form of:
Code:
Illustrator 9-10-04003
Illustrator 9-10-04004
Illustrator 9-10-04005
Illustrator 9-10-04006

If so, the loop might look like this:

Code:
#!/bin/bash

#hacker_renamer

cd [PUT THE DIRECTORY HERE]

find . -type f -maxdepth 1 -print0 | while read -d $'\0' file
do
  head -10 "$file"   # use the loop variable for the name

  newnam=`head -10 "$file" | grep "%%Title" | cut -d" " -f2"`

  printf "mv -n %s %s\n" "$file" "$newnam"
done

Try and inspect that the individual "mv" statements are good.

Once you are satisfied, pipe the output back to shell like so:

./hacker_renamer | /bin/bash
# 10  
Old 03-03-2010
Quote:
Originally Posted by kidney514
Here is the output

Code:
root# for fn in `ls illustrator*`;   do   head -10 "$fn"   newnam=`head -10 "$fn" | grep "%%Title" | cut -d" " -f2"`   mv "$fn" "$newnam"; done
ls: illustrator*: No such file or directory
root# ls
.DS_Store			Illustrator 9-10-04003.eps

That error means exactly what it says. You are trying to loop over a list of file names that begin with the word "illustrator" but there are none (at least not in the current working directory).

Quote:
Originally Posted by joeyg
What are the files currently called?
Quote:
Originally Posted by kidney514
Code:
Illustrator 9-10-04003
Illustrator 9-10-04004
Illustrator 9-10-04005
Illustrator 9-10-04006
and so on

Try "Illustrator*" instead. UNIX is case sensitive. "illustrator*" is not the same as "Illustrator". If that was an issue, the following should print out a paged list of the filenames:

Code:
for f in Illustrator*; do echo "$f"; done | more

If that seems to work, you can try:
Code:
for f in Illustrator*; do name=$(sed -n '/^\(%%Title: \)/{s///p;q;}' "$f"); [ "$name" ] && echo mv "$f" "$name"; done

... and if the list of mv commands looks good, you can remove the "echo". Execute at your own risk Smilie

Alister

Last edited by alister; 03-03-2010 at 07:34 PM..
# 11  
Old 03-03-2010
Quote:
Originally Posted by alister
Hello, all:

I see several solutions using the following snippet...
Code:
for fn in `ls file*`

... and just wanted to suggest not to do that. After the command substitution, filenames will undergo word splitting. If any of them include whitespace (assuming a default IFS value), the value of fn will not be set correctly for each filename (such filenames will be assigned to fn piecemeal over multiple loop iterations).

Code:
for fn in file*

... will accomplish the task without word splitting issues.

Illustrative example:
Code:
$ touch without_space with\ space

#Incorrect
$ for i in `ls w*`; do echo "$i"; done
with
space
without_space

#Correct
$ for i in w*; do echo "$i"; done
with space
without_space

Regards,
Alister
You are only partially correct. It is deceptive, but your second form is also incorrect in this case.

Try this:

Code:
for i in `ls`; do ls -l "$i"; done

that fixes the space problem, but breaks with file names with other characters, such as common ones of single or double quotes or mean ones like tab or CR.

The other problem is if you use more complex command substitution, such as this:

Code:
for i in "`ls | grep '^D'`"; do  echo "$i"; done

the return is:
Code:
Desktop
Documents
Downloads
Drive Util

which seems correct. However, now run:

Code:
for i in "`ls | grep '^D'`"; do wc -l "$i"; done

The return now is:

Code:
wc: Desktop\nDocuments\nDownloads\nDrive Util: open: No such file or directory

This is because BASH has passed all the file names with carriage returns to wc and wc thinks it is a single file name. Bad result especially with mv etc...


The only robust way I have found is with a ASCIZ termination and only while loops in bash support that...

---------- Post updated at 03:35 PM ---------- Previous update was at 03:30 PM ----------

Quote:
Originally Posted by alister
Try "Illustrator*" instead. UNIX is case sensitive. "illustrator*" is not the same as "Illustrator". If that was an issue, the following should print out a paged list of the filenames:

Code:
for f in Illustrator*; do echo "$f"; done | more

Alister
He is on a Mac, and the default is NOT case sensitive. Yours is better form however. On Mac BASH you can have case sensitive string comparisons tell you one thing and the file system do something disastrously different...
# 12  
Old 03-03-2010
This seems to works, will try on a small batch and keep you guys posted!
Quote:
Originally Posted by alister
That error means exactly what it says. You are trying to loop over a list of file names that begin with the word "illustrator" but there are none (at least not in the current working directory).




Try "Illustrator*" instead. UNIX is case sensitive. "illustrator*" is not the same as "Illustrator". If that was an issue, the following should print out a paged list of the filenames:

Code:
for f in Illustrator*; do echo "$f"; done | more

If that seems to work, you can try:
Code:
for f in Illustrator*; do name=$(sed -n '/^\(%%Title: \)/{s///p;q;}' "$f"); [ "$name" ] && echo mv "$f" "$name"; done

... and if the list of mv commands looks good, you can remove the "echo". Execute at your own risk Smilie

Alister


---------- Post updated at 07:09 PM ---------- Previous update was at 06:52 PM ----------

ok did a small test, and works well, but I just had to be french (lol) and need to support all the french special caracters like טיאח and one of the file contained a "ט" and resulted in a "%8" instead! Can this be resolve?
# 13  
Old 03-04-2010
Hi, drewk:

Quote:
Originally Posted by drewk
You are only partially correct. It is deceptive, but your second form is also incorrect in this case.
Nope, it's not. Everything that I said in that post is 100% correct.

Quote:
Originally Posted by drewk
Try this:

Code:
for i in `ls`; do ls -l "$i"; done

that fixes the space problem...
That does not fix any whitespace problems. If anything, that code is terribly broken and suffers from the shortcomings (unwanted word splitting breaking filenames into pieces) that I spoke of earlier.

Proof:
Code:
#Create two files in an empty directory, each of whose name contains a space
$ touch '1 2' '3 4'

#Your code that supposedly fixes the space problem
$ for i in `ls`; do ls -l "$i"; done
ls: 1: No such file or directory
ls: 2: No such file or directory
ls: 3: No such file or directory
ls: 4: No such file or directory

#The correct way to do that
$ for i in *; do ls -l "$i"; done
-rw-r--r--   1 xxxx  xxxx  0 Mar  4 12:16 1 2
-rw-r--r--   1 xxxx  xxxx  0 Mar  4 12:16 3 4

Regarding...
Quote:
Originally Posted by drewk
Code:
for i in "`ls | grep '^D'`"; do  echo "$i"; done
...snip...
for i in "`ls | grep '^D'`"; do wc -l "$i"; done

...snip...
This is because BASH has passed all the file names with carriage returns...
...the issues you present have absolutely nothing to do with injected carriage returns, but with the fact that the double quoted command substitution will ALWAYS evaluate to a single word regardless of how many files are in the directory. Those for loops are pointless and equivalent to:
Code:
echo "`ls | grep '^D'`"
wc -l "`ls | grep '^D'`"

If you can't see that and you still think I'm mistaken, perhaps the 'Useless Use of' links @ https://www.unix.com/shell-programmin...#post302400942 will help. If that still doesn't do it, read sh man page and/or posix sh documentation (particularly the sections on word splitting, quoting, and command substitution) and experiment until you see it.

Cheers,
Alister

P.S. By the way, since the tone of a message can easily be misinterpreted online, I just wanted to make it clear that I responded to your post in detail to help you understand and to ensure that no one else who has read this thread makes the same mistakes. It was not intended as an "i must win this Internet argument" type of response. Smilie I hope it helped.

Last edited by alister; 03-04-2010 at 07:20 PM..
# 14  
Old 03-04-2010
Power

Quote:
Originally Posted by alister
Hi, drewk:

snip snip snip

P.S. By the way, since the tone of a message can easily be misinterpreted online, I just wanted to make it clear that I responded to your post in detail to help you understand and to ensure that no one else who has read this thread makes the same mistakes. It was not intended as an "i must win this Internet argument" type of response. Smilie I hope it helped.
You are correct, I typed faster than I was thinking, and I certainly did not mean to offend in any way. I appreciate your detailed response. My examples of the for loop were not good -- granted and I acknowledge that your examples work as advertised.

You did not comment on the ASCIZ string form that I stated was an alternative. I do think that ASCIZ strings are better in many cases, especially when using find in the form of:

Code:
find . -type f -maxdepth X -print0 | while read -d $'\0' file
do
   do stuff
done

Login or Register to Ask a Question

Previous Thread | Next Thread

10 More Discussions You Might Find Interesting

1. Shell Programming and Scripting

Oop to copy and rename files through SQL Statement in shell Script

#!/bin/sh sqlplus -s "/ as sysdba" << EOF SET HEADING OFF SET FEEDBACK OFF Select pt.user_concurrent_program_name , OUTFILE_NAME FROm apps.fnd_concurrent_programs_tl pt, apps.fnd_concurrent_requests f where pt.concurrent_program_id = f.concurrent_program_id and pt.application_id =... (1 Reply)
Discussion started by: usman_oracle
1 Replies

2. Shell Programming and Scripting

SBATCH trinity for multiple files and rename/move the output files

Hey guys, I have wrote the following script to apply a module named "trinity" on my files. (it takes two input files and spit a trinity.fasta as output) #!/bin/bash -l #SBATCH -p node #SBATCH -A <projectID> #SBATCH -n 16 #SBATCH -t 7-00:00:00 #SBATCH --mem=128GB #SBATCH --mail-type=ALL... (1 Reply)
Discussion started by: @man
1 Replies

3. UNIX for Dummies Questions & Answers

Rename multiple files in shell bash, changing elements order.

Hi, I want to rename several files like this: example: A0805120817.BHN A0805120818.BHN ..... to: 20120817.0805.N 20120818.0805.N ...... How can i do this via terminal or in shell bash script ? thanks, (6 Replies)
Discussion started by: pintolcv
6 Replies

4. Shell Programming and Scripting

Rename multiple files

Hi, In my directory I have many files, for e.g. file_123 file_124 file_125 file_126 file_127 Instead of renaming these files one by one, I would like to rename them at a same time using same command... they should appear like 123 124 125 126 127 What command(awk or ls or... (3 Replies)
Discussion started by: juzz4fun
3 Replies

5. Shell Programming and Scripting

Rename multiple files

hello: I have multiple files with names like: somestring_y2010m01d01 somestring_y2010m01d02 .......... somestring_y2010m12d31 How... (4 Replies)
Discussion started by: sylcam
4 Replies

6. Shell Programming and Scripting

Rename the multiple files

Hi I need to reanme the multiple file using unix script I have multiple file like: sample_YYYYMMDD.xls test new_YYYYMMDD.xls simple_YYYYMMDD.xls I need to rename this file sample.xls testnew.xls SIMPLE.xls thanks (8 Replies)
Discussion started by: murari83.ds
8 Replies

7. Shell Programming and Scripting

Shell script to rename a group of files

Hello, I am having 1800 files in a directory with a specified format, like amms_850o_prod.000003uNy amms_850o_prod.000003u8x amms_850o_prod.000003taP amms_850o_prod.000003tKy amms_850o_prod.000003si4 amms_850o_prod.000003sTP amms_850o_prod.000003sBg amms_850o_prod.000003rvx... (12 Replies)
Discussion started by: atlantis
12 Replies

8. Shell Programming and Scripting

Shell Script to rename files

Hi, i need a bit of help writting a tcsh script which renames all ascii text files in the current directory by adding a number to their names before the extension so for example, a directory containing the files Hello.txt Hello.t Hello should have the following changes, Hello.txt... (2 Replies)
Discussion started by: yakuzaa
2 Replies

9. Shell Programming and Scripting

Shell script to rename files with .1,.2,.3 ....ext respectively

Hey Guys.... Just need some help as I am not proficient in Unix shell script... Doubt: --------------- Suppose there will be some of the following files inside a directory called OUT ... Path: - /appdb1/product/batch/rms/OUT files inside OUT directory:- POSU_75002_20090127_20090129035442... (4 Replies)
Discussion started by: satyajit007
4 Replies

10. Shell Programming and Scripting

rename multiple files

Hi, can anyone have a ksh script to rename multiple files (ie to remove .Z extension of the files) can someone correct this? for i in *.Z do var1 = substr($i, 1,at(".Z",$i)-1) mv $i $var1 done Thanks.. Antony (13 Replies)
Discussion started by: antointoronto
13 Replies
Login or Register to Ask a Question