LinuxQuestions.org
Visit Jeremy's Blog.
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 01-28-2007, 07:43 PM   #1
tworkemon
LQ Newbie
 
Registered: Jan 2007
Posts: 8

Rep: Reputation: 0
Script to move directories based on first letter to a new directory of that letter


Basically what i am trying to do is this.

/Blah/A.blah.blah
/Blah/A.blah2.blah2
/Blah/B.blah.blah
/Blah/B.blah2.blah2
/Blah2/A2.blah.blah
/Blah2/A2.blah2.blah2
/Blah2/B2.blah.blah
/Blah2/B2.blah2.blah2

I want to move all the A* directories into /A and all the B* directories into B. Now each one of those have files in them which I would like to move as well.

I've tried
Code:
find . -type d -iname "A*" -exec mv {} /A \;
but that sometimes gives me weird Files/Directories are not found and doesn't create one of the directories but puts the file of that directory in /A vs /A/A.blah.blah .

Any help with this script would be great or how to automate this better.
 
Old 01-28-2007, 08:46 PM   #2
tworkemon
LQ Newbie
 
Registered: Jan 2007
Posts: 8

Original Poster
Rep: Reputation: 0
After messing around with this some more, I still havent been able to automate this but this is what I have.
Code:
find . -type d -iname "A*" | cut -f 3 -d "/"| sed '/^[   ]*$/d'| awk '{print "mv " $1 " /A/"}' > move.sh
I am outputting this to a file now because when I do it this way I dont get the funky File/Directory not found message.

I would like to somehow automate this so I dont have to go down the list of A-Z and just run one script and it would do it.
 
Old 01-28-2007, 08:58 PM   #3
wjevans_7d1@yahoo.co
Member
 
Registered: Jun 2006
Location: Mariposa
Distribution: Slackware 9.1
Posts: 938

Rep: Reputation: 30
A quick and dirty way would be to have a script containing 26 lines, one for each letter of the alphabet, and on each line, instead of saying:

> move.sh

you say:

>> move.sh

to append any new commands to the end of the already-existing file. If you do this, you'll want an additional three lines at the beginning:

rm -f move.sh
touch move.sh
chmod 700 move.sh

The first of those lines will remove any move.sh that exists from a prior run. The second one creates an empty move.sh which you can execute (because you've changed the protection to 700) without error even if it's empty.

Two improvements are suggested.

The first is to suppress error messages, if you wish. The phrase

2> /dev/null

is recommended. Try experimenting with it in different places in the script, so the only time error messages are suppressed is if there are no files beginning with a certain letter. If you want to suppress errors at a particular point in piped commands, use parentheses (surrounded by spaces). This works by firing off a subshell to get the work done, but you don't need to know that. Example of a piped command where only a certain command in the middle has its error messages suppressed, but the other error messages appear, is this:

alpha | ( beta 2> /dev/null ) | gamma | delta

The other thing I'd suggest (but it's less important) is that if you want to not have 26 separate commands for the 26 letters of the alphabet, try this example:

for xxx in A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
do
echo $xxx
done

... and then plug in your command, using $xxx instead of A. Notice that the xxx is not preceded by the dollar sign on the "for" line, but is preceded by the dollar sign elsewhere.

Hope this helps.

Last edited by wjevans_7d1@yahoo.co; 01-28-2007 at 09:01 PM.
 
Old 01-29-2007, 10:45 AM   #4
tworkemon
LQ Newbie
 
Registered: Jan 2007
Posts: 8

Original Poster
Rep: Reputation: 0
Ok so I am taking your advice and im still having some problems

At first I was trying with
Code:
awk '{print "mv " $1  $i}' > test2
but I couldnt figure out how to get $i to work within awk. So now I am trying something like this. I want the mkdir and mv command to go to file for now so I can make sure it works properly. I am sure its something simple.

Any help would be great ... Thanks

Code:
#!/bin/bash
for i in A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
do
Dir=`find . -type d -iname $i* |cut -f 3 -d "/"`
echo "$Dir"| sed '/^[   ]*$/d'
echo mv $Dir $i/
#mkdir /$i >>test2
#mv $DIR /$i >>test2
done
Also I am not sure how to put a debug in case if there is already a directory with one of the $i variables.

Last edited by tworkemon; 01-29-2007 at 11:13 AM.
 
Old 01-29-2007, 02:28 PM   #5
tworkemon
LQ Newbie
 
Registered: Jan 2007
Posts: 8

Original Poster
Rep: Reputation: 0
After further testing, this is what I have now... I noticed that at times my previous code would want to move the main directory prior to moving its subdirectories. I am trying to put in an IF statement to ignore just a mv /ABC /A and only do mv /ABC/ABC2 /A

Code:
#!/bin/bash
for i in A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
do
mkdir -p /$i
find . -type d -iname "$i*" -exec echo "mv {} /$i" \;

if  [ "$#" < "2" ]
        echo "Will not move directory"
else
        #This is where I want to output the find command from above to a file
fi
done

Last edited by tworkemon; 01-29-2007 at 03:47 PM.
 
Old 01-30-2007, 04:31 AM   #6
wjevans_7d1@yahoo.co
Member
 
Registered: Jun 2006
Location: Mariposa
Distribution: Slackware 9.1
Posts: 938

Rep: Reputation: 30
I'm not sure what your current question is (grin), but I'd like to focus on something you said earlier:

Quote:
At first I was trying with

Code:
awk '{print "mv " $1 $i}' > test2
but I couldnt figure out how to get $i to work within awk.
What's in your way is not awk. It's how the shell is interpreting $1 and $i.

What you want the shell to do is substitute a particular letter for the $i, but pass the $1 to awk just as it is.

Try this:

Code:
for xxx in A B C
do
  echo literal: '$xxx'
  echo expanded: "$xxx"
done
See the difference in the output when you tried that? The shell will leave everything enclosed in single quotes alone, but process the dollar signs in double quotes. In either case, the shell doesn't pass the surrounding quotation marks themselves to awk; what awk sees is everything between them (expanded by the shell or left alone, depending on what you tell the shell to do).

What you shouldn't do is this:

Code:
awk "{print "mv " $1 $i}" > test2
for two reasons. The first reason is that you want to pass just one string to awk, the string between the first and final " in that line. But you have two other " in there as well, and bash will match the first with the second, and the third with the fourth.

Try this:

Code:
echo "abc \"def\" ghi"
See what happend with the internal quotes when you tried that? So the following is an improvement ...

Code:
awk "{print \"mv \" $1 $i}" > test2
... but not quite good enough yet. The shell is gonna do the right thing with the $i, but it's gonna mess up the $1. You want the $1 to go to awk without the shell messing with it. What makes the shell spring into action, in this case unwanted action, is the dollar sign. Try this:

Code:
for xxx in A B C
do
  echo expanded: "$xxx"
  echo literal: "\$xxx"
done
See the difference in the output when you tried that? The shell will not process a dollar signs in double quotes if it's "escaped" with a backslash.

So what you want is this:

Code:
awk "{print \"mv \" \$1 $i}" > test2
As you've probably guessed up to now, in figuring out how things are quoted and expanded by the shell before being passed to other programs such as awk, the echo command is your friend.

Incidentally, I'd recommend that you stay away from single-letter variables such as i. A problem arises if later you use a text editor to look for each occurrence of that variable. You'll also find all other i's in the script, even if they're part of a comment or the i in "if", for example. That's why I used xxx in my prior post.

Hope this gets you closer to where you want to be.
 
Old 01-30-2007, 08:23 AM   #7
tworkemon
LQ Newbie
 
Registered: Jan 2007
Posts: 8

Original Poster
Rep: Reputation: 0
Thanks, that did help and I did learn from that. I still cant get the $xxx variable to print from the awk command. Ideally I would like it to ignore just the /Blah from the outputs and only use /Blah/A.Blah. I guess I would like to use the Find command to only search through subdirectories and out put full path ??

My current code is this
Code:
#!/bin/bash
for xxx in A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
do
#mkdir -p /$xxx
Dir=`find . -type d -iname $xxx* | cut -f 1-3 -d "/"`
echo "$Dir" | sed '/^[   ]*$/d' | awk "{print \"mv \" \$1 $xxx}" >>test2
done
The output is this right now. As you can see it is still missing the $xxx variable at the end of each line. I tried various " and \ with $xxx and couldnt get it. Any additional help would be great.
Code:
mv /Blah
mv /Blah/A.Blah
mv /Blah2
mv /Blah2/A2.Blah
My main goal is to search through
Code:
/Blah
/Blah/A.Blah
/Blah2
/Blah2/A2.Blah
and only Find the subdirectories and move them to a main directory starting with its first letter. So the output would be this
Code:
mv /Blah/A.Blah /A
mv /Blah2/A2.Blah /A

Last edited by tworkemon; 01-30-2007 at 08:30 AM.
 
Old 01-30-2007, 08:54 AM   #8
tworkemon
LQ Newbie
 
Registered: Jan 2007
Posts: 8

Original Poster
Rep: Reputation: 0
Ok after some googling I found the -mindepth variable with Find. I am still now able to output $xxx through awk. With this I dont need the Cut command. I still need the sed to remove empty/blank lines.
Code:
!/bin/bash
for xxx in A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
do
#mkdir -p /$xxx
Dir=`find -mindepth 2 -type d -iname $xxx*`
echo "$Dir" | sed '/^[   ]*$/d' | awk "{print \"mv \" \$1 $xxx}" >> test2
done
Code:
mv /Blah/Z.blah
mv /Blah/A.Blah
mv /Blah2/A2.Blah
mv /Blah2/Z2.blah
 
Old 01-30-2007, 07:18 PM   #9
tworkemon
LQ Newbie
 
Registered: Jan 2007
Posts: 8

Original Poster
Rep: Reputation: 0
I finally got something I can work with Thanks for all the help

Code:
#!/bin/bash

rm movedirs.sh
touch movedirs.sh
chmod +x movedirs.sh

for xxx in A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
do
echo "mkdir -p ./$xxx" >> movedirs.sh
find -mindepth 2 -type d -iname "$xxx*" -exec echo "mv {} ./$xxx" \; >> movedirs.sh
done
 
  


Reply


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
Very simple shell script - move files to different directories beley Programming 7 11-02-2006 05:24 AM
Shell script - letter stat kovattila Programming 2 11-15-2005 01:48 PM
assign an arbitrary name or letter to a directory Melsync Linux - General 3 10-06-2005 09:48 AM
shell script: delete all directories named directory.# except directory.N brian0918 Programming 3 07-13-2005 06:54 PM
Letter........ nautilus_1987 General 5 12-25-2002 05:26 PM


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

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