1

So I am working on a program that delete files and folders recursively. For some reason I am stucked in endless loop. Please point out where I am wrong.

for file in $1
       do
               if [ -f $file ] #If it is a file just delete it
               then
                       echo "delete $file"

               elif [ -d $file ] #if it is a folder
               then
                        #look inside and see if it is empty or not
                       if [ "$(ls -A $file)" ] 

                       then
                                recursive $file #recursively call 
                       else
                                #if the folder is empty just delete it
                               echo "delete $file"
                       fi
               fi

       done
Wai Hein
  • 69
  • 2
  • 8
  • 9
    Is there a reason you don't want to use `rm -rf /path/to/folder`? The details of what -rf does can be found with `rm --help`. – Prix Sep 18 '13 at 02:58
  • Not sure, but I think you might need to `cd` into the next folder before you make the recursive call. –  Sep 18 '13 at 03:00
  • 1
    And exclude `..` from the loop or you will run into serious trouble running it recursively. – Prix Sep 18 '13 at 03:01
  • @Prix: I think using `for file in $1` will not include hidden files (like `.` and `..`) by default, right? – justhalf Sep 18 '13 at 03:07
  • @justhalf I guess it depends on how the argument is for example `/*` may grab those. But I would still prefer to use `rm` instead of reinventing the wheel unless there is a reason not to or that you would need to make your own function. – Prix Sep 18 '13 at 03:28
  • @Prix: Hmm, `/*` doesn't grab hidden files, although `.*` does. I agree that we should use `rm` if we _just_ want to delete the files. Dunno what other requirements the OP has in mind. – justhalf Sep 18 '13 at 03:31
  • 1
    @justhalf well you can use `-rf` to recursively delete an entire folder so unless he gives us more information... – Prix Sep 18 '13 at 03:34
  • The requirement is that I am building a function that deletes all the files and folders recursively. Lets call the script my_rm. When I type in my_rm -r filename, the script will recursively delete files and then recurse down the subfolders in the directory called "bin". I know I can just use built in command but there is no fun in it. – Wai Hein Sep 18 '13 at 04:05
  • I am new to UNIX scripting too. Learned it like a week ago.... – Wai Hein Sep 18 '13 at 04:06

3 Answers3

3

You have

for file in $1

...

recursive $file #recursively call

This effectively turns into repeating

for file in $file

That is, it keeps repeating for same first dir entry. You need to think the whole function again, either pass different parameter in recursive call, or do not use for over $1. Simplest may be to change the for (untested):

for file in "$1/*" "$1/.*"

Also add test for case where $file is . or .., or remove $file/.* part from my for.


About debugging bash scripts, following two set options are often useful, you can for example add them to start of your script.

set -x # print every final command line when it is executed
set +x # turn -x off

set -v # print script lines as they are read
set +v # turn -v off

For more about this, see this question.

Community
  • 1
  • 1
hyde
  • 60,639
  • 21
  • 115
  • 176
  • I used for file in $(tree -ifa -L 1 $1 --noreport | sort -r) and still get in infinite unless I actually delete the files instead of echoing out. Is there like a better way to rewrite it? I am pretty much out of idea for now – Wai Hein Sep 18 '13 at 04:19
  • @WaiHein Well, hard for me to debug without actually writing a script and running it, but see the edit about doing the debugging yourself. But most likely the first item in `for` is the directory itself... to avoid that, you use tail to filter out first line, untested example: `$(tree -ifa -L 1 $1 --noreport | tail -n+2 | sort -r)` – hyde Sep 18 '13 at 05:43
2

If I understand what you're trying to achieve, I suspect you'd be better off using the find command instead of rolling your own shell script.

For example, the following command should delete "just files" named foo.txt recursively starting at the current directory, and show you the list of files being deleted.

find . -name foo.txt -type f -exec rm -v {} \;

You can man find for details on how to use this tool, but basically the options are along the lines of:

find path [path ...] [expression]

where path is one or more start directories for your recursive search, and expression is a list of conditions or actions to perform.

In the example above, we have two obvious conditions -- -name foo.txt denotes the filename, and -type f says "this is a normal file". The third option in the expression executes a command line terminated with an escaped semicolon, replacing {} with the name of each file found.

Also, when processing multiple filenames, beware not to parse the output of ls. For details read ParsingLs from Greg's wiki.

Lastly, if you're trying to create a script that will take multiple filenames to delete, you could go with something like this:

#!/bin/sh

for file in $@; do
  find . -type f -name "$file" -exec rm -v {} \;
done

Note that this will also work in simpler POSIX style shells, it doesn't require bash.

ghoti
  • 45,319
  • 8
  • 65
  • 104
0

So I kinda rewrote it. The code is not that efficient but it does the work.

recursive()
{
        # create a file
        touch $HOME/temp_file #create a file
        tree -fi $1 > $HOME/temp_file #tree it up and pump it in the file

        touch $HOME/temp_file
        tree -fid $1 > $HOME/temp_file #tree it up and pump it in the file

        count=$(wc -l $HOME/temp_file | cut -d " " -f1)

        #loop thru the file that contains only directory 
        for ((i=1;i<=$[$count-2];i++))
        do
               args=$(sed -n $i"p" $HOME/temp_file) #grab line by line
               if [ -d $args ] #if it is a folder
                then
                        if recurse_down $args # if recurse down interactive flag is set
                        then
                                if  [ "$(ls -A $args)" ] #if dir is not empty
                                then
                                        touch $HOME/temp_file2
                                #store only file name in another file this time
                                ls $args | grep -v ^d > $HOME/temp_file2

                                        count2=$(wc -l $HOME/temp_file2 |  cut -d " " -f1)
                                        for ((z=1;z<=$count2;z++))
                                        do
                                                file=$(sed -n $z"p" $HOME/temp_file2)
                                                echo "deleting  $file"

                                                #call delete function and pass in filename
                                                # and the path of it
                                                delete $file $args  
                                        done
                                else
                                        echo "Empty dir. Just delete it"
                                        delete $args
                                fi
                         else
                                exit
                        fi

                fi



         done


}

delete()
{
        # $2 for path and $1 for filename
        echo "1 is $1"
        echo "2 is $2"
        File=$2/$1
        echo "Fileeeeeeee is $File"
        #echo "node is $node"

        node=$(stat -c%i $File) #get inode number of the file
        opFile="$recycleBin"/$(basename "$File")"_$node" #append it
        echo "opFile is $opFile"
        no_basename=$(basename "$File") #get basename

        # it will be like f8_1287540:/home/waiyan.hein/bin/d9/d1/f8
        echo "${no_basename}_$node:`pwd`/$File" >> $HOME/.restore.info

        #now move the file into destination folder
        mv $File $opFile


}
Wai Hein
  • 69
  • 2
  • 8