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 07-25-2010, 03:39 AM   #1
acvoight
LQ Newbie
 
Registered: Jul 2010
Distribution: Linux Mint
Posts: 21

Rep: Reputation: 1
while read line + cd trouble?


Okay, so I have two problems here:
  1. cd "$line" returns "No such file or directory" (e.g. ./brbooks: line 32: cd: Wetland Plants: No such file or directory, even though I know it is there and can see it..)
  2. The file $ls_file has Books ($ls_file) as the first line for some reason

Basically I have some files named ch1.pdf, ch2.pdf, ch3.pdf, etc. in different folders and I want to rename them to Chapter 1.pdf, Chapter 2.pdf, Chapter 3.pdf, etc. If you need more information, please ask, and sorry if this is a dumb question.. this is my first attempt at bash scripting.

My script:
Code:
#!/bin/bash

#Print some help if needed
if [[ $1 == "-h" || $1 == "--help" ]]
then
  echo "Usage:"
  echo "        ./batch_rename <directory> <chapters | appendices | covers>"
  exit
fi

#Declare some variables..
ls_file=Books

#Make sure <directory> is a directory
if [[ ! -d "$1" || -L "$1" ]]
then
  echo "Invalid directory"
  exit
fi

#Then, navigate to it..
cd "$1"
#..and list contents to a file
ls > $ls_file

#Check to see what we need to rename..
if [[ $2 == chapters ]]
then
  #..and rename it
  while read line
    do
    cd "$line"
    rename 's/^ch/Chapter /' *
    cd ..
    done <$ls_file
  echo "Renamed ch# to Chapter #"
  exit
elif [[ $2 == appendices ]]
then
  #..or rename this..
  while read line
    do
    cd "$line"
    rename 's/^ax/Appendix /' *
    cd ..
    done <$ls_file
  echo "Renamed ax[a-z] to Appendix [a-z]"
  exit
elif [[ $2 == covers ]]
then
  #..or rename this
  while read line
    do
    cd "$line"
    rename 's/^fmatt/Cover (Front)/' *
    cd ..
    done <$ls_file
  echo "Renamed fmatt to Cover (Front)"
fi

Last edited by acvoight; 07-25-2010 at 04:18 AM.
 
Old 07-25-2010, 04:07 AM   #2
acvoight
LQ Newbie
 
Registered: Jul 2010
Distribution: Linux Mint
Posts: 21

Original Poster
Rep: Reputation: 1
Okay, after talking with a friend and some experimenting around a bit, I found the problem to my first problem.. apparently when calling cd "$line" I wasn't actually in directory $1. However, my new question is why this is? And how did the while loop pull in the correct $ls_file for arguments (which it did) if it isn't in the directory I specified? Is there a way to make sure it stays in directory $1 without putting cd "$1" right before cd "$line"?

I am still wondering what is happening with Problem #2 though.

Last edited by acvoight; 07-25-2010 at 04:15 AM.
 
Old 07-25-2010, 04:14 AM   #3
druuna
LQ Veteran
 
Registered: Sep 2003
Posts: 10,532
Blog Entries: 7

Rep: Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405
Hi,

Problem #1:

You are in $1 because you navigated into it:
Quote:
#Then, navigate to it..
cd "$1"
After that $ls_file is filled:
Quote:
#..and list contents to a file
ls > $ls_file
Maybe problem #2 is related to this (not sure, have to take a better look to figure that out).

Hope this gets you going again.
 
Old 07-25-2010, 04:16 AM   #4
acvoight
LQ Newbie
 
Registered: Jul 2010
Distribution: Linux Mint
Posts: 21

Original Poster
Rep: Reputation: 1
Quote:
Originally Posted by druuna View Post
Hi,

Problem #1:

You are in $1 because you navigated into it:
Sorry, that is a typo on my part in my reply (and not being specific enough in original post), I meant to say I am not actually in $1, even though I have cd'ed there.

Last edited by acvoight; 07-25-2010 at 04:19 AM.
 
Old 07-25-2010, 04:21 AM   #5
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 10,007

Rep: Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191
Quote:
sorry if this is a dumb question
The only dumb question is the one not asked

So I thought I would work from the top down:
Code:
#Make sure <directory> is a directory
if [[ ! -d "$1" || -L "$1" ]]
Your remark is not the same as your test as the '-L' is looking to see if it is a symbolic link which may not necessarily be a link to a directory
Code:
cd "$1"
Have you quoted this to retain spaces or other unusual characters? If these are not likely the probably not required.
Code:
cd "$line"
This appears to be the offending line. The directory you are in has only sub directories and zero files? (ie Wetland Plants is a directory?)
Code:
    cd "$line"
    rename 's/^ch/Chapter /' *
    cd ..
Why? What I mean to say is that instead of changing in and out of directories, just call your command on the entire path:
Code:
rename 's/^ch/Chapter /' "$line/*"
 
Old 07-25-2010, 04:21 AM   #6
druuna
LQ Veteran
 
Registered: Sep 2003
Posts: 10,532
Blog Entries: 7

Rep: Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405Reputation: 2405
Hi,

How do you know this? After the script is finished you end up where you started it.

Add the blue part to your code to see this:
Code:
#Then, navigate to it..
cd "$1"
echo $(pwd)
#..and list contents to a file
ls > $ls_file
A script is executed as a child-shell and everything in the script is done in this child-shell.

Hope this helps.
 
Old 07-25-2010, 04:38 AM   #7
acvoight
LQ Newbie
 
Registered: Jul 2010
Distribution: Linux Mint
Posts: 21

Original Poster
Rep: Reputation: 1
Quote:
Originally Posted by grail View Post
The only dumb question is the one not asked

Code:
cd "$line"
This appears to be the offending line. The directory you are in has only sub directories and zero files? (ie Wetland Plants is a directory?)
Code:
    cd "$line"
    rename 's/^ch/Chapter /' *
    cd ..
Why? What I mean to say is that instead of changing in and out of directories, just call your command on the entire path:
Code:
rename 's/^ch/Chapter /' "$line/*"
Okay, I've worked through this and fixed the comment (me being lazy) and quotes around $1 (there will be no spaces in that directory).

$1 contains all directories $line but I get an error unless I say cd $1 inside the while..do..done loop, like so:
Code:
    do
    cd $1
    cd "$line"
    done <$ls_file
then I don't get an error, which is odd, because I inserted that echo ${PWD} after cd $1 and as the first statement in my while..do..done loop (removing the cd $1 in the loop) and it said my directory was $1 each time! So needless to say I am very confused why it isn't working correctly without saying cd $1 in the while loop.

As for doing it all on one line, that doesn't seem to work even with a cd $1 in the while loop (I tried "$line/*" and "${line}/*"), while my brutish way seems to work with the cd $1 in the loop.

And the $1 has one file ($ls_file) and two subdirectories, atm (it's just a testing directory.. don't want to change any actual files until I'm sure it works).

Hope everything I've said so far makes sense, and thanks for your replies.

Last edited by acvoight; 07-25-2010 at 04:57 AM.
 
Old 07-25-2010, 09:58 AM   #8
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 10,007

Rep: Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191
I think all the changing of directories is just causing problems. Let us try a different attack.

The assumption of this script is that you will run it and provide the correct path, this being the name of a book.
Once this name has been provided, within each book directory are one / all of chapters, appendices or covers as file names.
So let us try something like:
Code:
#!/bin/bash

#Print some help if needed
if [[ $1 == "-h" || $1 == "--help" ]]
then
  echo "Usage:"
  echo "        ./batch_rename <directory> <chapters | appendices | covers>"
  exit
fi

#Make sure <directory> is a directory
if [[ ! -d $1 ]]
then
  echo "Invalid directory"
  exit
fi

case $2 in
    chapter)	old="ch"; new="Chapter";;
    appendices)	old="^ax"; new="Appendix";;
    covers)	old="^fmatt"; new="Cover {Front)";;
    *) echo "You have not entered a valid choice!"
		exit;;
esac

while read line
do
	mv -v $line ${line/%$old*/$new}${line##*$old}
done< <(find $1 -type f -name "$old*")
Explanation is as follows:

1. I have chosen case over if only to make it a little cleaner, but you can change back.

2. As you can see the case now simply sets some variables to be used in a once off while loop as opposed to several

3. I have changed from rename to mv because during my testing I had a directory with the letters ch in it and it tried to change the name of the directory first which then caused an issue when trying to find the file. Also, because I am using paths and not changing into each directory I am unable to use the
start of line regex '^' as the line does not start with, for example, ch but rather just the file name I want to change which is not elsewhere in the string.

4. Lastly, I am using find instead of ls as it is more robust and also allows me to choose files only (from the -type f switch)

I am not saying this is the only way, but hoping it will help lead you to the solution you are looking for.
 
1 members found this post helpful.
Old 07-25-2010, 01:06 PM   #9
acvoight
LQ Newbie
 
Registered: Jul 2010
Distribution: Linux Mint
Posts: 21

Original Poster
Rep: Reputation: 1
Wow grail, thanks! I appreciate the help, and it seems like it should work (find gives me the the correct thing). However, it doesn't work with spaces in the directory names (e.g. if "Wetland Plants" is a directory). Moreover, even removing the spaces it doesn't work for some reason? I get, for example:
Code:
mv: target `12.pdf' is not a directory
mv: target `21.pdf' is not a directory
mv: target `8.pdf' is not a directory
mv: target `16.pdf' is not a directory
mv: target `2.pdf' is not a directory
mv: target `6.pdf' is not a directory
..you get the point
in a directory like WetlandPlants containing ch1-ch25.

Also, I have a couple questions:
This line ${line/%$old*/$new}${line##*$old}. I read up on ${var##} sometime last night.. does the ##*$old remove that from $line? Also, what does the ${line/%$old*/$new} do? Why are those two things adjacent?
Why does done have an extra < next to it?

Thanks for the tip regarding case, that works really well (and I now know what esac means!)

Last edited by acvoight; 07-25-2010 at 01:08 PM.
 
Old 07-25-2010, 01:57 PM   #10
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 10,007

Rep: Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191
No problems ... wasn't sure whether there would be spaces or not. The following changes should work:
Code:
#!/bin/bash

#Print some help if needed
if [[ $1 == "-h" || $1 == "--help" ]]
then
  echo "Usage:"
  echo "        ./batch_rename <directory> <chapters | appendices | covers>"
  exit
fi

#Make sure <directory> is a directory
if [[ ! -d books/$1 ]]
then
  echo "Invalid directory"
  exit
fi

case $2 in
	chapter)	old="ch"; new="Chapter";;
	appendices)	old="^ax"; new="Appendix";;
	covers)		old="^fmatt"; new="Cover {Front)";;
	*) echo "You have not entered a valid choice!"
		exit;;
esac

while read line
do
	mv -v "$line" "${line/%$old*/$new}${line##*$old}"
done< <(find "books/$1" -type f -name "$old*"
${line/%$old*/$new} - replace everything at the end of the line starting with $old with value for $new - book/book_name/ch1.txt becomes book/book_name/Chapter

${line##*$old} - remove everything up until the old name, ie leave the number and extension - ch1.txt becomes 1.txt

Put both together to get new name - book/book_name/Chapter1.txt

Space and extra <() allows the find command to redirect output into while loop a line at a time.
 
2 members found this post helpful.
Old 07-25-2010, 03:00 PM   #11
acvoight
LQ Newbie
 
Registered: Jul 2010
Distribution: Linux Mint
Posts: 21

Original Poster
Rep: Reputation: 1
That worked like a charm (after some slight tweaking), thanks grail! I really appreciate you helping out and answering all my questions
 
Old 07-25-2010, 03:41 PM   #12
acvoight
LQ Newbie
 
Registered: Jul 2010
Distribution: Linux Mint
Posts: 21

Original Poster
Rep: Reputation: 1
Actually, I guess I have one further problem, when I went and actually did this, it failed for some of the book folders that had hyphens in them (but not all):
Algae-Anatomy, Biochemistry, and Biotechnology
Quantum-Mechanical Signal Processing and Spectral Analysis
Quantum Mechanics-An Introduction for Device Physicists and Electrical Engineers, Second Edition
The Fluid Mechanics of Astrophysics and Geophysics-Earth's Core and Lower Mantle
The Fluid Mechanics of Astrophysics and Geophysics-Fluid Dynamics and Dynamos in Astrophysics and Geophysics
(instead of placing it in the book folder, it placed it in the Folder containing the book folders, and renamed them to Algae-AnChapter 1.pdf, Quantum-MeChapter 1.pdf, Quantum MeChapter 1.pdf The Fluid MeChapter 1.pdf and The Fluid MeChapter 1.pdf, respectively, for all chapters)
Is this because -A or -M or such are.. control characters (I believe is the right word?), and make mv or find do something special? Is there a way to protect against this, for future reference? But if that is the case, why didn't it do it for the folder Circuits & Filters Handbook 3e-Analog and VLSI Circuits?
(A link to a reference guide would be fine as well)

Last edited by acvoight; 07-25-2010 at 04:05 PM.
 
Old 07-25-2010, 06:28 PM   #13
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 10,007

Rep: Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191
It would be to do with the naming of those directories. Not sure about the first one:

Algae-AnChapter 1.pdf

But the other make sense as they have the letters ch at the point where there is a replacement:

Quantum-Mechanical Signal Processing and Spectral Analysis -> Quantum-MeChapter 1.pdf
Quantum Mechanics-An Introduction for Device Physicists and Electrical Engineers, Second Edition -> Quantum MeChapter 1.pdf
The Fluid Mechanics of Astrophysics and Geophysics-Earth's Core and Lower Mantle -> The Fluid MeChapter 1.pdf
The Fluid Mechanics of Astrophysics and Geophysics-Fluid Dynamics and Dynamos in Astrophysics and Geophysics -> The Fluid MeChapter 1.pdf

Circuits & Filters Handbook 3e-Analog and VLSI Circuits -> No ch

This is the culprit part - ${line/%$old*/$new}

The link I use is this one

I will let you play with it and see how you go ... If you get really stuck let us know.
 
1 members found this post helpful.
Old 07-25-2010, 06:49 PM   #14
acvoight
LQ Newbie
 
Registered: Jul 2010
Distribution: Linux Mint
Posts: 21

Original Poster
Rep: Reputation: 1
For Algae, that is my misremembering, is actually turned the files into Algae-Anatomy, BioChapter 1.pdf (sorry if I was misunderstood, I meant the files inside the directory, e.g. ch1.pdf ch2.pdf ch3.pdf (to ch8.pdf) that were in Algae-Anatomy, Biochemistry, and Biotechnology were renamed to Algae-Anatomy, BioChapter 1.pdf, Algae-Anatomy, BioChapter 2.pdf and Algae-Anatomy, BioChapter 3.pdf, (to Chapter 8.pdf) and were then placed in $1).

However, I did find a simple fix (using regular expressions), which is to change old to something like ch[0-9] since ch will only have a number after it.

Thanks again for your help grail!

Edit - here is the final script:
Code:
#!/bin/bash

#Print some help if needed
if [[ $1 == "-h" || $1 == "--help" ]]
then
  echo "Usage:"
  echo "        ./brbooks <directory> <chapters|appendices|cover>"
  exit
fi

#Make sure <directory> is a directory
if [[ ! -d $1 ]]
then
  echo "Invalid directory"
  exit
fi

case $2 in
  chapters)    match="ch[0-9]"; old="ch"; new="Chapter ";;
  appendices)  match="ax[a-z]"; old="ax"; new="Appendix ";;
  cover)       match="fmatt"; old="fmatt"; new="Cover (Front)";;
  *)           echo "You have not entered a valid choice!"
               exit;;
esac

while read line
do
  mv -v "$line" "${line/%$old*/$new}${line##*$old}"
done< <(find "$1" -type f -name "$match*")

Last edited by acvoight; 07-25-2010 at 07:11 PM.
 
Old 07-25-2010, 08:33 PM   #15
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 10,007

Rep: Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191
Good work
 
  


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 : read every line from text file starting at given line number quadmore Programming 4 02-20-2009 12:29 PM
I would like need a suggestion on bash shell : Read a file line by line and do stuff madi3d8 Linux - Newbie 1 01-15-2009 09:30 AM
help with c program to read each line from text file, split line , process and output gkoumantaris Programming 12 07-01-2008 12:38 PM
php - Read file line by line and change a specific line. anrea Programming 2 01-28-2007 01:43 PM
linux scripting help needed read from file line by line exc commands each line read atokad Programming 4 12-26-2003 10:24 PM

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

All times are GMT -5. The time now is 03:56 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