LinuxQuestions.org
Review your favorite Linux distribution.
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 11-16-2005, 04:10 PM   #1
linmix
Senior Member
 
Registered: Jun 2004
Location: Spain
Distribution: FC5
Posts: 1,993
Blog Entries: 1

Rep: Reputation: 46
bash 'prepend' script


I've just started having a serious look at bash scripting and have sofar found my sig to be a great resource.

I'm trying to make a substitution script to prepend a 0 to a group of audio files. So far I've come up with this:

Code:
#!/bin/bash
#
#test script to prepend a 0 to any type of file

echo "This script will prepend a 0 to any file"
echo "or group of files specified "
echo
echo "Specify path / file"
read files

for i in "$files" ; do
	cp "$i" "0$i"
done
exit
cp will be replaced by mv once the script works.

The reason why I'm using double quotes around the variables is that most filenames contain spaces. However, the script will still not work on those files.
Code:
$ ~/prepend0.sh
This script will prepend a 0 to any file
or group of files specified
 
Specify path / file
./*ogg
cp: cannot stat `./*ogg': No such file or directory
however:
Code:
$ ls
00-Lords_and_Ladies.ogg
00 - Lords and Ladies.ogg
apparently there's a problem with the way I read the files ito the script. If I give individual file names it works fine, but what I need it to indicate a folder (and possibly an extention type) and convert all the files that correspond to the pattern.

./*.ogg will not work, nor will *.ogg or anything else I've tried.

I've found a way to use sed to extract the filenames I need:
Code:
files=`ls -l | sed 's/\([^ ]*[ ]*\)\{8\}\([.]*\)/\2/' | grep .ogg`
for i in "$files" ; do
	cp "$i" "0$i"
done
exit
but this returns an error because instead of supplying individual file names to the for function it concatenates the filenames like file1.ogg/file2.ogg/file3.ogg

What am I doing wrong? Am I making things too complicated?
 
Old 11-16-2005, 05:47 PM   #2
Dave Kelly
Member
 
Registered: Aug 2004
Location: Todd Mission Texas
Distribution: Linspire
Posts: 215

Rep: Reputation: 31
See if there is anything in this script that helps.
Code:
for i in *jpg; do echo $i; convert -font helvetica -pointsize 144 -fill "#0432ef" -draw "text 600, 600 'Lazy Cat'" $i $i; done
 
Old 11-16-2005, 06:00 PM   #3
homey
Senior Member
 
Registered: Oct 2003
Posts: 3,057

Rep: Reputation: 61
How about this...
Code:
#!/bin/bash
#Example: ./test /home/images
usage()
{
   echo "Usage: $0 Directory_Name"
   exit 1;
}

test -d "$1" || usage
dir="$1"

ls $dir | \
while read i ; do
  j=`echo "0$i"`
  mv -v "$dir/$i" "$dir/$j"
done
 
Old 11-16-2005, 09:45 PM   #4
Firewing1
LQ Newbie
 
Registered: Nov 2005
Location: Canada
Distribution: Fedora 12
Posts: 6

Rep: Reputation: 0
Hey,
Thanks for the link linmix, i'm not a member of LinuxQuestions.org. It's because you have quotes; the system is taking it literally:
you entered
./*
as $files. Because you said
cp "$files"
the quotes make the system think the ./* is literall, and a file is really called ./* and not all the files in the current dir. You should use the sed method or use a # find command to find the files, then cp them.
Firewing1
 
Old 11-19-2005, 04:12 AM   #5
linmix
Senior Member
 
Registered: Jun 2004
Location: Spain
Distribution: FC5
Posts: 1,993

Original Poster
Blog Entries: 1

Rep: Reputation: 46
Thanks all for your help and interest. I'll study all your suggestions as I'm sure there's a lot to learn from them.

Firewing: welcome to LQ!
Won't I get in trouble if i don't use double quotes because of spaces in the filenames? I'll check anyway.

Dave I'm not sure what your script does, but I like the approach o setting 'i' as any 'jpg' file in the directory. that would enable the selection I was looking for to subsequently enter into the 'for' loop

homey I see what you mean and what I like about it is that it becomes a script with arguments. I had a look at using 'ls' myself, but that could cause a problem if there were another directory inside the one I'm trying to process. I tried coming up with a line including sed to cicumvent this problem, but it won't work yet
Code:
find -type f -maxdepth 1 -exec echo `sed 's/\(\.\/\)\(\[.\]*\)/\2/'` ';'
Thanks again guys. I'm learning a lot!
 
Old 11-19-2005, 09:11 AM   #6
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
This is how I would do it:
Code:
#!/bin/bash

echo "Files please:"
read files
ls $files | while read f ; do
	mv -v "$f" "0$f"
done
Test run:
Code:
bash$ touch 111.txt 222.txt 333.txt qq\ qq.txt
bash$ rename.sh
Files please:
*.txt
`111.txt' -> `0111.txt'
`222.txt' -> `0222.txt'
`333.txt' -> `0333.txt'
`qq qq.txt' -> `0qq qq.txt'
Note: no problem with filename with spaces ("qq qq.txt")
 
Old 11-19-2005, 12:04 PM   #7
Dave Kelly
Member
 
Registered: Aug 2004
Location: Todd Mission Texas
Distribution: Linspire
Posts: 215

Rep: Reputation: 31
Quote:
Originally posted by linmix
[
Dave I'm not sure what your script does, but I like the approach o setting 'i' as any 'jpg' file in the directory. that would enable the selection I was looking for to subsequently enter into the 'for' loop
It is not important what the script does. It is important that you read the answers to your question and also the scripts others have written and understand the solutions put before you. In this case you picked up on the correct part. Looks like you did not know what to call it. Its called a 'for loop', work horse of the coding world.

Quote:
Firewing: welcome to LQ!
Won't I get in trouble if i don't use double quotes because of spaces in the filenames? I'll check anyway.
I too thought this was true. Let me offer a snippet of code someone showed me that proves different.

I am reading a file with entries that take this flavor.
Code:
http://mirror.narfum.org/LDP/*Karel Bemelmans <xxxxxx@xxxxm.org>*
http://www.linwiz.com/ldp/*Roland Venter <linxxxxxxxxx@xxxxxxxxx.com>*
http://www.bibsyst.no/LDP/* Torkel Hasle <toxxxxxxxx@xxxxxxxxx.no>*
Code:
main()
{
cat starlist | ( \
     IFS=* ; while read url admin; \
     do check_link $url $admin; done \
     )
}


check_link()
{
   url=$1
   admin_email=$2

   printf "URL is: %s\n" $url
   printf "Admin is: %s\n" $admin_email

       

}
This should be a good exercise to dig and comment this snippet of code.

Dave
 
Old 11-19-2005, 03:46 PM   #8
linmix
Senior Member
 
Registered: Jun 2004
Location: Spain
Distribution: FC5
Posts: 1,993

Original Poster
Blog Entries: 1

Rep: Reputation: 46
Firewing1 I checked on the double quotes but it makes no difference: tried "$i" $i and ${i} and everything gives me the same error messages. The problem is with the reading in of the files.

I tried sed (as included in the first post) but the sed command doesn't work properly yet as it concatenates file names instead of giving them to me 1 by 1.

Hko Thanks for your input. I've been trying to find out why you used a while loop instead of a for loop and, frankly, I don't see the difference.:0 It works though, so thanks a mil!

It's also solved a problem I had with mv because it complained the second parameter wasn't a directory + I like the idea of using a pipe: it's really compact.

Dave Way over my head ... for now However I'm determined to make sense of it all one day.

------------------------------------------------------------------

Unfortunately I only have acces to a dual-boot box with no internet access on the linux side so it's a little complicated to check everything immediately. I will though

--------------------------------------------------------------------

As a little encore:

I've started reading the Advanced Scripting Guide . Section 2.2 is titled 'Preliminary Exercises' and states:

"Write a script that upon invocation shows the time and date, lists all logged-in users, and gives the system uptime. The script then saves this information to a logfile."

So I produced this:
Code:
#!/bin/bash
logfile=~/homework.txt
echo `date` > $logfile 
echo `who` >> $logfile
echo `uptime` >> $logfile
exit
which works fine, BUT, I only send the info to a file and don't show it in the terminal. After reading about redirection of stout I haven't found a way to both display and redirect. Any ideas?
 
Old 11-19-2005, 03:53 PM   #9
Firewing1
LQ Newbie
 
Registered: Nov 2005
Location: Canada
Distribution: Fedora 12
Posts: 6

Rep: Reputation: 0
Try this to find files:
files="$files $(find $i)"
then cp files...
Firewing1
 
Old 11-19-2005, 04:38 PM   #10
linmix
Senior Member
 
Registered: Jun 2004
Location: Spain
Distribution: FC5
Posts: 1,993

Original Poster
Blog Entries: 1

Rep: Reputation: 46
could you 'subtitle' that?
 
Old 11-19-2005, 07:41 PM   #11
Firewing1
LQ Newbie
 
Registered: Nov 2005
Location: Canada
Distribution: Fedora 12
Posts: 6

Rep: Reputation: 0
I'm sorry, don't follow you, but I'll show you the code:
Well, this is the problem with BASH. it can't tell the diff between literal and unliteral things, which is why PHP is the language of my choice. I ususally let users enter either a complete path or ./*, and PHP will add the backslashes where needed. So, i can do
cp $file 0${file}
and not worry about spaces, cause PHP adds backslashed before spaces with a function i invented.
But, if you REALLY want to do it in bash, then you'll either
1) Use # zenity --file-selection --separator=";" and make BASH consider after each ; a new file
(this makes literal paths) OR,
2) make it non-literal (no quotes on variables) but echo a warning saying to the user to backslash ( \ ) spaces, and any other special character:

#!/bin/bash
echo "Please execute 'prepend --usage' for correct usage, if you haven't checked already."
for arg;do
if [ "$arg" == "--usage" ]
then
echo "WARNING: Please backshash ( \ ) any space in the filename or before any special character.
EG: '/home/admin/space file', when you type the path, becomes '/home/admin/space\ file'.

You can enter multiple file by seperating them with a space."
exit
fi
if [ "$arg" == "--help" ]
then
echo "WARNING: Please backshash ( \ ) any space in the filename or before any special character.
EG: '/home/admin/space file', when you type the path, becomes '/home/admin/space\ file'.

You can enter multiple file by seperating them with a space."
exit
fi
cp $arg $(find $arg -printf %h)/$(basename $arg)0
done
if [ "$1" = "" ]
then
file=
echo -n "File : ";read file
cp $file $(find $file -printf %h)/$(basename $file)0
fi
This will
1) If --usage or --help is used, show help message and exit
2) If there's args, (commandname arg1 arg2 arg3) then copy those and exit.
3) if --usage/--help is not specified, and not paths are specified as args, promp user to enter path.
Firewing1
 
Old 11-20-2005, 08:01 AM   #12
eddiebaby1023
Member
 
Registered: May 2005
Posts: 378

Rep: Reputation: 33
Quote:
So, i can do
cp $file 0${file}
and not worry about spaces
Sounds like you need to learn how to use quotes in the shell.
 
Old 11-20-2005, 08:28 AM   #13
linmix
Senior Member
 
Registered: Jun 2004
Location: Spain
Distribution: FC5
Posts: 1,993

Original Poster
Blog Entries: 1

Rep: Reputation: 46
Quote:
Originally posted by Firewing1
I'm sorry, don't follow you,

Firewing1
I simply can't get my head around files="$files $(find $i)" Don't see where it comes into the script and where it gets its input from. So files is the same as itself plus + the output of "find $i". But has i been declared before then? In that case what does find look for?.

Anyway, I have saved your reply to read it again as soon as I'm back in linux. It's a pain booting back and forth to be able to connect and then check, but it can't be helped for now. Glad I can use a computer at all (I have strict doctors orders not to move for a week!! so I'll have to make do with a borrowed laptop)
 
Old 11-20-2005, 08:29 AM   #14
linmix
Senior Member
 
Registered: Jun 2004
Location: Spain
Distribution: FC5
Posts: 1,993

Original Poster
Blog Entries: 1

Rep: Reputation: 46
I liked the simplicity of Hko's sollution best so that's the first thing I tried. It works nicely, but only if you're in the same directory the files are in. homey's sollution however allows me to run th script from anywhere I want with a commandline option to indicate the location of the files + an error message. I liked the idea so I have tried to expand on it.

While reading about bash scripting in RUTE (see my sig.) I found the 'case' argument used to implement options so I have extended the script to include these. For the moment the -r and -a options are not very useful because they add or remove part of the end of the filename without taking into account possible extentions.

Another problem I'm working on is the fact that the script treats files and directories alike, so if there's a directory in the path idicated to the script its name will also be changed. I don't want that to happen so I'm looking at a way to change
Code:
ls $dir | while read i ; do
to something along the lines of:
Code:
ls $dir | grep ^- | \
sed -e 's/<input>/<output>'/| \
while read i ; do
but I'm still battling with this. So far
Code:
sed 's/^\([^ ]*[ ]*\){8}\([^ ]\)/\2/'
looks like it should work, but gives me no results

For now the script looks like this:
Code:
#!/bin/bash
#Example: ./test /home/images
usage()
{
   echo "Usage: $0 [OPTION]... DIRECTORY"
   echo "    -a|--append <symbols>    add <symbols> to the"
   echo "                             end of the filename"
   echo "    -h|--help                print this message"
   echo "    -p|--prepend <symbols>   add <symbols> to the"
   echo "                             beginning of the filename"
   echo "    -r|--remove <symbols>    remove <symbols> from the"
   echo "                             beginning of the filename"
   echo "    -s|--shorten <symbols>   remove <symbols> from the"
   echo "                             end of the filename"
   exit 2;
}
case $1 in
    -h|--help)
        usage
        exit 0
    ;;
    -a|--append)
        test -d "$3" || usage
        app="$2"
        dir="$3"
        ls $dir | while read i ; do
          j=`echo "$i$app"`
          mv -v "$dir/$i" "$dir/$j"
        done
        exit 0
    ;;
    -p|--prepend)
        test -d "$3" || usage
        app="$2"
        dir="$3"
        ls $dir | while read i ; do
          j=`echo "$app$i"`
          mv -v "$dir/$i" "$dir/$j"
        done
        exit 0
    ;;
    -r|--remove)
        test -d "$3" || usage
        app="$2"
        dir="$3"
        ls $dir | while read i ; do
          j=`echo "${i##$app}"`
          mv -v "$dir/$i" "$dir/$j"
        done
        exit 0
    ;;
    -s|--shorten)
        test -d "$3" || usage
        app="$2"
        dir="$3"
        ls $dir | while read i ; do
          j=`echo "${i%%$app}"`
          mv -v "$dir/$i" "$dir/$j"
        done
        exit 0
    ;;
    -*)
        echo "No such option $1"
    ;;
esac

usage
${i%%$app} and ${i##$app} are supposed to admit wildcards, but if I allow for this option I should also find a way to allow wildcards using the -a and -p options.

... and I'll have to come up with a sligtly better name for it
 
Old 11-21-2005, 04:16 PM   #15
linmix
Senior Member
 
Registered: Jun 2004
Location: Spain
Distribution: FC5
Posts: 1,993

Original Poster
Blog Entries: 1

Rep: Reputation: 46
I've been trying a lot of different options with sed, but all with no result. Since I couldn't find what was going wrong I went back to my book (as per sig.) and studied the examples. Still didn't see what was wrong.

Then I tried the examples out in a vt by cutting and pasting (no typos supposedly) and still got no results!!

Due to copyright reasons I do not want to quote too much, but the example given was this:
Quote:
Now test to see what happens when you run this:
Code:
sed -e 's/\(<[^ ]*>\)\([ ]*\)\(<[^ ]*>\)/\3\2\1/g'
GNU Linux is cool
Linux GNU cool is
My terminal doesn't return the words in inverse order. So either my terminal has abnormal behaviour, or something is wrong with the command. The strange thing is that none of the sed commands given in the book work for me, which is, to say the least, suspicious.

Can anyone tell me if the sed command works for them?

------------------------------------------------------------

anyway, I thought I could solve it in a much easier way using awk:
Code:
ls -l | grep ^- | awk '{print $9}'
but athough this allows me to eliminate directories from my search it will not match filenames with spaces
 
  


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
Prepend # for certain line numbers in VI twantrd Linux - Software 3 09-05-2005 01:27 PM
prepend char to a string schneidz Programming 6 06-01-2005 08:53 AM
send automatic input to a script called by another script in bash programming jorgecab Programming 2 04-01-2004 12:20 AM
bash script - incrementing a filename in a script tslinux Programming 10 08-05-2003 11:58 PM
bash script prob: how can i tell the script that a 'dd' has finished? Frustin Linux - General 2 04-02-2003 05:34 AM

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

All times are GMT -5. The time now is 04:49 PM.

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