0

I am almost done with the script but this outputs directories. What I would like is to have an output of files. Anyway one of you guys wanna help me out? :)

    #!bin/bash
( find /testftp/* -type d ;
  find /testftp/* -type f -iname DONOTDELETE.TXT -printf '%h'
) | sort | uniq -u

Output is:

/testftp/logs

The output is the directory of where DONOTDELETE.TXT doesn't exist. It's pretty close. Just need to display the files.

7
  • (1) You want to delete files only in a directory that does not have DONOTDELETE.TXT, but not the directory itself? (2) Which directory set does the printf display now? The ones with the TXT file or the ones without it?
    – ADTC
    Feb 24, 2012 at 17:31
  • (1) Yes! (2) printf displays the directory where the DONOTDELETE.TXT DOESN'T exist. BUT it also displays "/testftp" which is the directory containing the other subdirectories. So its pretty close but I'm lose now LOL Feb 24, 2012 at 17:33
  • Try -exec list {}/* instead of -printf '%h'. If it shows the correct files only, maybe you can try -exec rm -f {}/* (Caution: This is dangerous!). Also, this maybe useful.
    – ADTC
    Feb 24, 2012 at 17:46
  • With this code ( find /testftp -type d ; find /testftp -type f -iname DONOTDELETE.TXT -exec list {} \; ) | sort | uniq -u Output is : ` find: list: No such file or directory /testftp /testftp/logger /testftp/logs ` DONOTDELETE.TXT is under ` /testftp/logger ` Feb 24, 2012 at 17:54
  • Sorry, that should have been ls not list (always forget it!)
    – ADTC
    Feb 24, 2012 at 18:14

3 Answers 3

1

Here's my take on it:

#! /bin/bash

SPECIAL_FILE=DONOTDELETE
LOGFILE=clean.$(date +"%Y-%d-%m.%H%M%S").log   
FIND_BASE="$1"
if [ $# -ne 1 ]; then
        echo "Syntax $(basename $0) <FIND_BASE>"
        exit 1
fi
if [ "$FIND_BASE" = "." ]; then
        FIND_BASE=$(pwd)
else
        FIND_BASE=$(pwd)/$FIND_BASE
fi

for d in $(find $FIND_BASE -type d -print); do
        if [ "$d" != "$FIND_BASE" ]; then
                ls $d | grep $SPECIAL_FILE &> /dev/null
                if [ $? -ne 0 ]; then
                        echo "Deleting $d" | tee -a $LOGFILE
                        rm -rf $d 2>/dev/null
                else
                        echo "Ignoring $d, contains $SPECIAL_FILE" | tee -a $LOGFILE
                fi
        fi
done
exit 0

Add this to a script, modify the variables with your own naming convention if you like (for the special file and the log name), and then just call it with the starting directory path as a parameter. It will exclude any directory containing the file you want, and delete all the rest.

0
1

Thought i'd give this one a go.


#!/bin/bash
# file name and path can not have spaces for this to work.

ignorefile=DONOTDELETE.TXT
dir="`find /testftp/* -type d`";
exists=$(ls $dir | for each in $(find $dir -type f -iname $ignorefile -printf '%h\n'); do echo -en "grep -v $each |" ; done | sed '$s/.$//') 
direxists=$(ls $dir | eval $exists | grep -v $ignorefile | sed 's/:/\//g' | sort | uniq -u)

for pth in $direxists; 
do 
if [ -d $pth ]; then 
if [ "$(ls -A $pth)" ]; then 
echo rm -f ""$pth*""
fi
fi
done

reproduce:
changed dir="`find /testftp/* -type d`";
to
dir="`find ./testftp/* -type d`";

mkdir testftp && cd testftp
for x in 1 2 3 4 5 6 7 8 9; do mkdir $x; done
for y in 1 2 3 4 6 7 9; do touch $y/blah; done
touch 5/DONOTDELETE.TXT
touch 5/some.log
touch 8/DONOTDELETE.TXT
touch 8/another.file

cd ..
$ ./script.sh 
rm -f ./testftp/1/blah
rm -f ./testftp/2/blah
rm -f ./testftp/3/blah
rm -f ./testftp/4/blah
rm -f ./testftp/6/blah
rm -f ./testftp/7/blah
rm -f ./testftp/9/blah

3
  • I tried this. ` #!/bin/bash # file name and path can not have spaces for this to work. ignorefile=DONOTDELETE.TXT dir="find ./testftp/* -type d"; exists=$(ls $dir | for each in $(find $dir -type f -iname $ignorefile -printf '%h\n'); do echo -en "grep -v $each |" ; done | sed '$s/.$//') direxists=$(ls $dir | eval $exists | grep -v $ignorefile | sed 's/:/\//g' | sort | uniq -u) for pth in $direxists; do if [ -d $pth ]; then if [ "$(ls -A $pth)" ]; then echo rm -f ""$pth*"" fi fi done ` But it didn't do anything :( Feb 24, 2012 at 20:44
  • any thoughts? :) Feb 24, 2012 at 21:34
  • what you pasted back in here and what i put up there are 2 different things. and if your using cygwin/windows forget it
    – tao
    Feb 24, 2012 at 22:39
0

If you have bash 4+ (check with bash --version), you can do this in a two-liner:

shopt -s globstar
for f in ./**/; do [[ -f "$f"/DONOTDELETE.TXT ]] || rm -f "$f"/*; done

Note that shopt -s globstar needs to be on its own line - don't just prepend it to the for loop with a ;.

./**/ expands to every subdirectory in the current directory, and their subdirectories recursively. If you only want to go down the tree a single level, use ./*/ instead (and don't bother setting globstar); if you want finer control than that, you'll have to mess around with find instead (specifically the -maxdepth and -mindepth options). I use ./**/ instead of **/ in case any of your directories begin with a -: this stops them from being seen as

[[ -f "$f"/DONOTDELETE.TXT ]] tests to see if that file exists and is a file (if you want it to work even if DONOTDELETE.TXT may be something other than a file, use -e instead of -f). Strictly speaking, you don't need the / in there, since $f contains a trailing slash, but I think it looks better this way, and in general redundant forward-slashes are harmless. || means OR - if (and only if) that test evaluates to false, then the code to the right of it will be executed, in this case rm -f "$f"/* - which deletes every file, except the hidden ones.

If you want to delete hidden files too, you can use something like:

for f in ./**/; do [[ -f "$f"/DONOTDELETE.TXT ]] || rm -f "$f"/* "$f"/.*; done

Not the answer you're looking for? Browse other questions tagged .