Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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? :)

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

Output is:


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

share|improve this question
(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 '12 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 – JoyIan Yee-Hernandez Feb 24 '12 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 '12 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 ` – JoyIan Yee-Hernandez Feb 24 '12 at 17:54
Sorry, that should have been ls not list (always forget it!) – ADTC Feb 24 '12 at 18:14
up vote 1 down vote accepted

Here's my take on it:

#! /bin/bash

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

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
                        echo "Ignoring $d, contains $SPECIAL_FILE" | tee -a $LOGFILE
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.

share|improve this answer
Thanks Yanick! This worked! :) – JoyIan Yee-Hernandez Feb 27 '12 at 17:01
You're welcome! – Yanick Girouard Feb 27 '12 at 17:25

Thought i'd give this one a go.

# file name and path can not have spaces for this to work.

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; 
if [ -d $pth ]; then 
if [ "$(ls -A $pth)" ]; then 
echo rm -f ""$pth*""

changed dir="`find /testftp/* -type d`";
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/some.log
touch 8/another.file

cd ..
$ ./ 
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

share|improve this answer
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 :( – JoyIan Yee-Hernandez Feb 24 '12 at 20:44
any thoughts? :) – JoyIan Yee-Hernandez Feb 24 '12 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 '12 at 22:39

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
share|improve this answer

You must log in to answer this question.

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