Linux - NewbieThis 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
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
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.
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.
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~
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 08:38 AM.
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 10:12 PM.
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".
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.
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 12:36 AM.
Reason: edited minor typos
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 07:23 AM.
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 08:25 AM.
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.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.