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-14-2009, 01:47 PM   #16
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957

Ok, that's a good start. You've got the idea of a variable down.

Now put a command or two in the for loop. A good test to start with is to use echo. The 'i' in the loop is a variable that will hold each line of input (in this case, the individual filenames in $directory) in turn. So try this:

Code:
#!/bin/bash

directory=/home/zsf/pictures
for i in $directory/* ; do

     echo "$i"

done
This should output a list of all the files in the directory. After you get a correct list of files with echo, you can add other commands in the same way; "cp $i /path/to/newdir/", for example. Basically, you use $i anywhere you want to insert the filename in the command. The loop will then run the command on each file in turn.
 
Old 02-14-2009, 02:34 PM   #17
theNbomr
LQ 5k Club
 
Registered: Aug 2005
Distribution: OpenSuse, Fedora, Redhat, Debian
Posts: 5,396
Blog Entries: 2

Rep: Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908
Search this and related forums on LQ for subjects containing bash. Doing this will bring you to many questions that are practical in nature, and virtually all will have solutions to the problem; as often as not there will be several solutions from different people (some of the solutions will actually work, too!). Try to develop your own solutions for such problems. You will be able to use what others have done to solve practical problems, and learn from them in the process. Eventually, you will be able to contribute solutions of your own. You will also accumulate ideas about how your new-found knowledge can be applied in practical ways for your own benefit.
--- rod.
 
Old 02-16-2009, 07:38 AM   #18
topheraholic
Member
 
Registered: Aug 2008
Location: shanghai
Distribution: ubuntu
Posts: 128

Original Poster
Rep: Reputation: 15
Quote:
Originally Posted by David the H. View Post
Ok, that's a good start. You've got the idea of a variable down.

Now put a command or two in the for loop. A good test to start with is to use echo. The 'i' in the loop is a variable that will hold each line of input (in this case, the individual filenames in $directory) in turn. So try this:

Code:
#!/bin/bash

directory=/home/zsf/pictures
for i in $directory/* ; do

     echo "$i"

done
This should output a list of all the files in the directory. After you get a correct list of files with echo, you can add other commands in the same way; "cp $i /path/to/newdir/", for example. Basically, you use $i anywhere you want to insert the filename in the command. The loop will then run the command on each file in turn.
thanks very much! and i still can not get it right. i do really want to generate a sequentially-numbered files.but i still do not know how to do this! please~
 
Old 02-16-2009, 08:36 AM   #19
theYinYeti
Senior Member
 
Registered: Jul 2004
Location: France
Distribution: Arch Linux
Posts: 1,897

Rep: Reputation: 61
Quote:
Originally Posted by topheraholic View Post
how to practice?
Consider any thread asking for help with a shell (bash/sed/awk/tr/) script as a test to your new knowledge. Answer and you'll get better.

Yves.
 
Old 02-16-2009, 09:35 AM   #20
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957
Ok, try this. Forget about writing a full script at first and just try it straight from the shell. Type these two commands:

Code:
i="hello"
echo $i
The output will be the text string stored in the variable "i".


Now let's try something different:

Code:
i=./*
echo i
Now "$i" contains a list of the directory contents, each separated by a space.


Next, let's create a "for" loop instead:

Code:
for i in ./* ; do echo $i ; done
This should give you a list of all the files in the current directory. Only now they're all on separate lines (because a separate echo command was run for each filename).

What the command is doing is taking the input list "./*" and feeding only the first entry (as separated by spaces) into the variable "i". Then it runs echo "$i", which outputs the value stored in "i". Finally, it loops back to the beginning and repeats the process on the second entry in ./*, then the third, etc., until there are no more entries left, at which point it exits.

You can replace the echo command with any other program or series of programs. everything between "do" and "done" will be executed on "i" for each entry in the filelist. For example, try replacing "echo $i;" with "file $i;" and see what happens.

Then try stringing multiple commands together and adding more detail, such as:

Code:
for i in ./*; do echo "what is $i?"; file $i; done
.

Finally, here's an interesting thing to try. After the for loop finishes, run "echo $i" again. The output should be the last entry in the directory filelist, which is still there because that was the last thing that went through the loop. Variables don't automatically get cleared after they are used.


Once you have the commands working, you can create a script. Place the them in a text file, with #!/bin/bash as the first line. Note that you can put commands on separate lines instead of using semicolons to break them up. Make that file executable with "chmod u+x myscript.sh", and run it with a simple "./myscript.sh". It should then do exactly the same thing your commands did when you did them manually.

This is not really difficult stuff here. You just have to make sure you understand how the different pieces go together. And for that you have to try it and practice it (just make sure you practice your commands in a safe test directory with files you can afford to lose, until you know that what you're doing is safe ).

Last edited by David the H.; 02-16-2009 at 09:38 AM.
 
Old 02-17-2009, 11:11 PM   #21
topheraholic
Member
 
Registered: Aug 2008
Location: shanghai
Distribution: ubuntu
Posts: 128

Original Poster
Rep: Reputation: 15
Quote:
Originally Posted by David the H. View Post
Ok, try this. Forget about writing a full script at first and just try it straight from the shell. Type these two commands:

Code:
i="hello"
echo $i
The output will be the text string stored in the variable "i".


Now let's try something different:

Code:
i=./*
echo i
Now "$i" contains a list of the directory contents, each separated by a space.


Next, let's create a "for" loop instead:

Code:
for i in ./* ; do echo $i ; done
This should give you a list of all the files in the current directory. Only now they're all on separate lines (because a separate echo command was run for each filename).

What the command is doing is taking the input list "./*" and feeding only the first entry (as separated by spaces) into the variable "i". Then it runs echo "$i", which outputs the value stored in "i". Finally, it loops back to the beginning and repeats the process on the second entry in ./*, then the third, etc., until there are no more entries left, at which point it exits.

You can replace the echo command with any other program or series of programs. everything between "do" and "done" will be executed on "i" for each entry in the filelist. For example, try replacing "echo $i;" with "file $i;" and see what happens.

Then try stringing multiple commands together and adding more detail, such as:

Code:
for i in ./*; do echo "what is $i?"; file $i; done
.

Finally, here's an interesting thing to try. After the for loop finishes, run "echo $i" again. The output should be the last entry in the directory filelist, which is still there because that was the last thing that went through the loop. Variables don't automatically get cleared after they are used.


Once you have the commands working, you can create a script. Place the them in a text file, with #!/bin/bash as the first line. Note that you can put commands on separate lines instead of using semicolons to break them up. Make that file executable with "chmod u+x myscript.sh", and run it with a simple "./myscript.sh". It should then do exactly the same thing your commands did when you did them manually.

This is not really difficult stuff here. You just have to make sure you understand how the different pieces go together. And for that you have to try it and practice it (just make sure you practice your commands in a safe test directory with files you can afford to lose, until you know that what you're doing is safe ).
oh thanks so much! i appreciate.
i know what your mean. but i am so stupid, i can not make it generate a sequental-numbered output. the only i can do is this:

#!/bin/bash

directory=/home/zsf/Pictures/h
for i in $directory/*
do
echo "what is $i?"
file $i
mv $i /home/zsf/Pictures/h/00
done

it can change one file name. i do not know how to change more than one file names. sorry!

Last edited by topheraholic; 02-17-2009 at 11:12 PM.
 
Old 02-18-2009, 07:16 AM   #22
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957
Please show us exactly what your input is (i.e. "ls" the directory), and what the output is after you run the script (the error messages). Also tell us what you've tried so far to work around your problem and what the outcome was. You have tried working it out yourself, haven't you?

One thing I can think of that might be affecting it is if your files contain spaces. Spaces in filenames can be a big headache when it comes to scripting, as spaces are usually viewed as field separators. Try putting quotes around all of your $1 instances so the contents will be seen as a single unit; e.g. file "$i".
 
Old 02-18-2009, 08:33 AM   #23
topheraholic
Member
 
Registered: Aug 2008
Location: shanghai
Distribution: ubuntu
Posts: 128

Original Poster
Rep: Reputation: 15
Quote:
Originally Posted by David the H. View Post
Please show us exactly what your input is (i.e. "ls" the directory), and what the output is after you run the script (the error messages). Also tell us what you've tried so far to work around your problem and what the outcome was. You have tried working it out yourself, haven't you?

One thing I can think of that might be affecting it is if your files contain spaces. Spaces in filenames can be a big headache when it comes to scripting, as spaces are usually viewed as field separators. Try putting quotes around all of your $1 instances so the contents will be seen as a single unit; e.g. file "$i".
sorry for giving you that thought that i was lazy.but i do try it.

after i run ls

the output is

3.jpg 4.jpg 5.jpg

i have tried

#!/bin/bash

directory=/home/zsf/c
for i in $directory/*
do
echo "what is $i?"
file $i
mv $i /home/zsf/c/new-name
done

and the output is a file named new-name.

i add one command mv $i /home/zsf/new-name, but it only can change one file name!
and i also tried this ${i%[1-9].jpg}.i know this is not complete.but i do not know how to do next.is that clear? thanks.
 
Old 02-19-2009, 01:31 AM   #24
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957
I see. The problem is that your mv command has only one fixed output name, so every file you run through it gets the same name. You're overwriting the same file over and over. For a straight move from one directory to another, change your mv command to

mv "$i" "/home/zsf/c/$i"

Which will give the output file the same name as the input file.

The ${} parameter substitution you tried to use is a good way to change the output filename, but you have the wrong structure. What ${i%.jpg} does is remove the string ".jpg" from the end of the variable, if it exists, leaving you with just the basename. After that you'll probably want to add something else back to the name, which you can simply tack onto the end of the variable.

For example, if you wanted to change all ".jpg" extensions to ".jpeg", you'd use this:

mv "$i" "${i%.jpg}".jpeg

You probably don't want to use a [0-9] (match any number) in a situation like this. As it is you were matching, and stripping, the entire filename, leaving you with a blank output.

So, to summarize, use the "$i" string everywhere in the script you want the filename to appear. This will usually be in both the input and output parts of the commands. To modify a filename, you can use the ${} parameter substitution (among other techniques). You generally won't want to do this on the input filename, because the substitution happens before the command is run, but it's great for altering the output name.

Last edited by David the H.; 02-19-2009 at 01:36 AM. Reason: edited minor typos
 
Old 02-19-2009, 08:20 AM   #25
topheraholic
Member
 
Registered: Aug 2008
Location: shanghai
Distribution: ubuntu
Posts: 128

Original Poster
Rep: Reputation: 15
Quote:
Originally Posted by David the H. View Post
I see. The problem is that your mv command has only one fixed output name, so every file you run through it gets the same name. You're overwriting the same file over and over. For a straight move from one directory to another, change your mv command to

mv "$i" "/home/zsf/c/$i"

Which will give the output file the same name as the input file.

The ${} parameter substitution you tried to use is a good way to change the output filename, but you have the wrong structure. What ${i%.jpg} does is remove the string ".jpg" from the end of the variable, if it exists, leaving you with just the basename. After that you'll probably want to add something else back to the name, which you can simply tack onto the end of the variable.

For example, if you wanted to change all ".jpg" extensions to ".jpeg", you'd use this:

mv "$i" "${i%.jpg}".jpeg

You probably don't want to use a [0-9] (match any number) in a situation like this. As it is you were matching, and stripping, the entire filename, leaving you with a blank output.

So, to summarize, use the "$i" string everywhere in the script you want the filename to appear. This will usually be in both the input and output parts of the commands. To modify a filename, you can use the ${} parameter substitution (among other techniques). You generally won't want to do this on the input filename, because the substitution happens before the command is run, but it's great for altering the output name.
does mv can use this way?? mv "$i" "${i%.jpg}".jpeg ? i only know it can use like this:
mv existing-filename new-filename

why i did not know that? where can i look up for?

Last edited by topheraholic; 02-19-2009 at 08:23 AM.
 
Old 02-19-2009, 08:47 AM   #26
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957
Quote:
Originally Posted by topheraholic View Post
does mv can use this way?? mv "$i" "${i%.jpg}".jpeg ? i only know it can use like this:
mv existing-filename new-filename

why i did not know that? where can i look up for?
The command is doing just what it's supposed to. "$i" is the existing filename, and "${i%.jpg}".jpeg is the new filename.

For example:
Code:
~$ i="img01.jpg"
~$ echo "$i"
img01.jpg
~$ echo "${i%.jpg}"
img01
~$ echo "${i%.jpg}".jpeg
img01.jpeg
So "mv $i ${i%.jpg}.jpeg" becomes "mv img01.jpg img01.jpeg" after the variable is evaluated.

A good way to test what the final command will look like after the variable evaluations are made is to enclose it in an echo statement.

Code:
~$ echo "mv $i ${i%.jpg}.jpeg"
mv img01.jpg img01.jpeg

Last edited by David the H.; 02-19-2009 at 08:58 AM.
 
Old 02-19-2009, 08:52 AM   #27
topheraholic
Member
 
Registered: Aug 2008
Location: shanghai
Distribution: ubuntu
Posts: 128

Original Poster
Rep: Reputation: 15
Quote:
Originally Posted by David the H. View Post
I see. The problem is that your mv command has only one fixed output name, so every file you run through it gets the same name. You're overwriting the same file over and over. For a straight move from one directory to another, change your mv command to

mv "$i" "/home/zsf/c/$i"

Which will give the output file the same name as the input file.

The ${} parameter substitution you tried to use is a good way to change the output filename, but you have the wrong structure. What ${i%.jpg} does is remove the string ".jpg" from the end of the variable, if it exists, leaving you with just the basename. After that you'll probably want to add something else back to the name, which you can simply tack onto the end of the variable.

For example, if you wanted to change all ".jpg" extensions to ".jpeg", you'd use this:

mv "$i" "${i%.jpg}".jpeg

You probably don't want to use a [0-9] (match any number) in a situation like this. As it is you were matching, and stripping, the entire filename, leaving you with a blank output.

So, to summarize, use the "$i" string everywhere in the script you want the filename to appear. This will usually be in both the input and output parts of the commands. To modify a filename, you can use the ${} parameter substitution (among other techniques). You generally won't want to do this on the input filename, because the substitution happens before the command is run, but it's great for altering the output name.
when i run this

mv "$i" "/home/zsf/Pictures/G/$i"


why it says "cannot move, no such file or directory?"
but this works:
mv "$i" "/home/zsf/Pictures/G/"

Last edited by topheraholic; 02-19-2009 at 09:25 AM.
 
Old 02-19-2009, 09:06 AM   #28
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957
Does the /home/zsf/Pictures/G/ directory exist? You can't move files into a nonexistant directory. You have to create it first.
 
Old 02-21-2009, 09:02 AM   #29
topheraholic
Member
 
Registered: Aug 2008
Location: shanghai
Distribution: ubuntu
Posts: 128

Original Poster
Rep: Reputation: 15
Quote:
Originally Posted by David the H. View Post
The command is doing just what it's supposed to. "$i" is the existing filename, and "${i%.jpg}".jpeg is the new filename.

For example:
Code:
~$ i="img01.jpg"
~$ echo "$i"
img01.jpg
~$ echo "${i%.jpg}"
img01
~$ echo "${i%.jpg}".jpeg
img01.jpeg
So "mv $i ${i%.jpg}.jpeg" becomes "mv img01.jpg img01.jpeg" after the variable is evaluated.

A good way to test what the final command will look like after the variable evaluations are made is to enclose it in an echo statement.

Code:
~$ echo "mv $i ${i%.jpg}.jpeg"
mv img01.jpg img01.jpeg
does this called stubbing? i just saw that from the website you give to me.
 
Old 02-21-2009, 12:59 PM   #30
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957Reputation: 1957
Quote:
Originally Posted by topheraholic View Post
does this called stubbing? i just saw that from the website you give to me.
Not exactly. Stubbing means inserting a quick, neutral line as a temporary placeholder, with the intent of coming back later and replacing it with something more useful. That way you can build the basic structure of the script before concentrating on the details of the individual commands. A real stub would be something like:

echo "Replace this line with the mv command".


The suggestion I gave you is more of a debugging test. It helps you see clearly what's happening so you can catch possible mistakes before you use the script on anything important. Sticking in echo lines for variables and commands is a good way to see what your code is really doing.
 
  


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
How to run root privileged Linux command as normal user via shell shell tcegrid Linux - Newbie 1 06-23-2008 04:38 PM
LXer: Terminal functions for shell scripting with Shell Curses LXer Syndicated Linux News 0 03-27-2008 12:50 AM
LXer: Shell tip: Set the shell prompt and themes in Linux Terminal LXer Syndicated Linux News 0 06-12-2007 04:02 AM
Alias or shell script to confirm 'exit' commands from a shell rose_bud4201 Programming 2 03-08-2006 03:34 PM
'sh' shell - Actually calls legacy Bourne shell, or uses system default? Dtsazza Linux - Software 1 10-28-2005 10:20 AM


All times are GMT -5. The time now is 02:43 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
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration