LinuxQuestions.org
Visit Jeremy's Blog.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 04-01-2010, 01:11 AM   #1
mago
Member
 
Registered: Apr 2004
Location: Costa Rica
Distribution: slack current with 2.6.16.18 (still off the hook)
Posts: 284

Rep: Reputation: 33
Bash, renaming files won't work


Hi, I'm trying to rename a lot of files getting rid of the space on the names. For that purpose I wrote this very simple bash script, but for some reason is not working.

Code:
for i in "$(ls)"
do
j=$(echo "$i" | sed 's/\ /_/g')
mv "$i" "$j"
done
But what I get in return for each line is just one long file name with all the file names concatenated.
I've tried with echo -e "$i" as well with no results.

This has to be something really simple that I'm missing but I just can't see it.

Thanks in advanced for any pointers.
 
Old 04-01-2010, 01:21 AM   #2
GrapefruiTgirl
LQ Guru
 
Registered: Dec 2006
Location: underground
Distribution: Slackware64
Posts: 7,594

Rep: Reputation: 556Reputation: 556Reputation: 556Reputation: 556Reputation: 556Reputation: 556
Hi there!

You're working along the right idea, but using `ls` for this is not a great idea. Go to LQ's Search Engine and enter:
Code:
rename filename spaces
into the keyword search field; select "Search Entire Posts" from the dropdown gadget below that text field, and click "Search Now" -- you will find many examples of how to do just what you're doing here. Adjust your search keywords as necessary, but I think you'll find the answer on one of the pages returned.

Sasha
 
Old 04-01-2010, 01:26 AM   #3
smeezekitty
Senior Member
 
Registered: Sep 2009
Location: Washington U.S.
Distribution: M$ Windows / Debian / Ubuntu / DSL / many others
Posts: 2,339

Rep: Reputation: 231Reputation: 231Reputation: 231
Try:
Code:
for i in "*.*"
do
j=$(echo "$i" | sed 's/\ /_/g')
mv "$i" "$j"
done
 
Old 04-01-2010, 02:39 AM   #4
colucix
LQ Guru
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509

Rep: Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983
Actually the reason why you get all file names concatenated together is that double quotes prevent field splitting (that is a string embedded in double quotes is seen as a single string, despite the presence of blank spaces). This means the loop is cycled only once and the loop variable get the value:
Code:
file one file two file three
Anyway, since you have file names with blank spaces, you have to find a way to prevent file names splitting in two or more pieces. One of them could be:
Code:
while read file
do
  echo "$file"
done < <(ls *\ *)
Here the "read" statement assigns every line of the input to the variable "file". Just use double quotes around "$file" inside the code block and the trick is done.
 
Old 04-01-2010, 02:40 AM   #5
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
Thanks to tuxdev in this LQ post a robust solution (not tested) is
Code:
while IFS="" read -r -d "" file ; do
   /usr/bin/mv "$file" "${file// }" 
done < <(find . -maxdepth 1 -type f -name '* *' -print0)
 
Old 04-01-2010, 02:44 AM   #6
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
Anither solution (slightly tested!) is to use plain file name expansion
Code:
for file in *
do
   [[ $file != ${file// } ]] && /usr/bin/mv "$file" "${file// }"
done
 
Old 04-01-2010, 03:08 AM   #7
colucix
LQ Guru
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509

Rep: Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983
An even more simple solution is mass renaming using the rename command. However, there are two versions of rename: one coming from util-linux for which you can simply do:
Code:
rename ' ' '_' *\ *
and another (coming from perl) which uses perl expressions, for which the syntax would be:
Code:
rename 's/ /_/g' *\ *
Looking at the manual page of rename, you can easily find out which version is installed on your system.

Last edited by colucix; 04-01-2010 at 03:10 AM.
 
Old 04-01-2010, 03:24 AM   #8
colucix
LQ Guru
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509

Rep: Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983
Quote:
Originally Posted by catkin View Post
Anither solution (slightly tested!) is to use plain file name expansion
Code:
for file in *
do
   [[ $file != ${file// } ]] && /usr/bin/mv "$file" "${file// }"
done
Hi catkin. Your solution works, except for the substitution of blank spaces with underscores. Hence, the substring replacement should be
Code:
"${file// /_}"
Surprisingly it works even if I do (but maybe this is not robust)
Code:
for file in *\ *
do
  mv "$file" "${file// /_}"
done
which is almost the same as yours, except the file list contains only names with spaces. But shouldn't it split each name in the file list into pieces?!

Last edited by colucix; 04-01-2010 at 03:31 AM.
 
Old 04-01-2010, 04:28 AM   #9
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,578
Blog Entries: 31

Rep: Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208Reputation: 1208
Quote:
Originally Posted by colucix View Post
But shouldn't it split each name in the file list into pieces?!
The closest to an explanation of how it does behave is here where in step 4 Bash "# Performs the various shell expansions (see Shell Expansions), breaking the expanded tokens into lists of filenames (see Filename Expansion) and commands and arguments". Filename Expansion comes after breaking the command up into IFS-character-separated words and a single glob can be expanded into several words, each of which may contain IFS-characters ...

EDIT:

The above is not accurate. Where I wrote "after breaking the command up into IFS-character-separated words" it should have been "after breaking the command up into metacharacter-character-separated words where a metacharacter is one of:
  • |
  • &
  • ;
  • (
  • )
  • <
  • >
  • space
  • tab

Last edited by catkin; 04-02-2010 at 12:08 AM.
 
Old 04-01-2010, 05:36 AM   #10
colucix
LQ Guru
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,509

Rep: Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983Reputation: 1983
Quote:
Originally Posted by catkin View Post
Filename Expansion comes after breaking the command up into IFS-character-separated words and a single glob can be expanded into several words, each of which may contain IFS-characters ...
Yesss! Quite clear, now. Thank you!
 
Old 04-01-2010, 07:59 PM   #11
ghostdog74
Senior Member
 
Registered: Aug 2006
Posts: 2,697
Blog Entries: 5

Rep: Reputation: 244Reputation: 244Reputation: 244
you can use Python
Code:
import os
os.chdir("/mypath")
for files in os.listdir("."):
    if os.path.isfile(files) and " " in files:
         newfile=files.replace(" ","_")
         os.rename(files,newfile)
 
Old 04-02-2010, 01:10 PM   #12
Olion
LQ Newbie
 
Registered: Mar 2010
Location: Germany
Posts: 24

Rep: Reputation: 15
Hi,
You can also use shell

Code:
$ ls -1
file one.txt
file three.txt
file two.txt
file1.txt
file2.txt
file3.txt
$ find . -name "*\ *" > x
$ cp x y
$ sed -i 's/.*/"&"/' x
$ tr " " "_" < y > z
$ paste x z | sed 's/^/mv /' > s; sh s
$ ls -1
file_one.txt
file_three.txt
file_two.txt
file1.txt
file2.txt
file3.txt
s
x
y
z
$
Greeting
Oleg

Last edited by Olion; 04-02-2010 at 02:32 PM.
 
  


Reply



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
Bash script for sorting and renaming multiple mp3 files by id3 tags simonloach Linux - General 8 02-16-2013 09:07 AM
Bash script for renaming files (all odd) snaggletooth1134 Programming 10 05-01-2012 12:26 AM
bash help renaming files kahn Programming 6 06-16-2005 07:15 AM
How do I make a bash sheet for renaming files? Cyberian Linux - Newbie 16 03-16-2005 08:08 AM
Renaming files doesn't work from console Mr. Gone Linux - Software 9 03-20-2004 07:59 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 05:03 AM.

Main Menu
Advertisement
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
Open Source Consulting | Domain Registration