The macosxhints Forums

The macosxhints Forums (http://hintsforums.macworld.com/index.php)
-   UNIX - Newcomers (http://hintsforums.macworld.com/forumdisplay.php?f=15)
-   -   delete all files except.... (http://hintsforums.macworld.com/showthread.php?t=100084)

kaptagat 03-25-2009 06:10 AM

delete all files except....
 
Hello Everyone

I need a script to run on logon which will delete everything in /Users/Shared/ except a folder called "Infected"

I have tried rm -R *.* !(Infected) but it comes back with !: event not found.

Secondly, to try to keep the students temporary folder reasonably "clean" I use a script like the one below :-

rm -R *.app *.zip *.dmg *.pkg

Can anyone suggest any other "nasty" extensions i should delete. The only stuff in there should be office 2008 work, CS4 and Quark work. I also run a script on this folder which deletes everything over 1 month old.

thanks

thanks

hayne 03-25-2009 09:21 AM

You should use a script (not a one-line command) with a loop and an 'if' statement to select which folders you want to delete (or not).

tlarkin 03-25-2009 09:52 AM

I have a search and destroy scripts for such things, but you will need to modify mine. You will have to set the path to the exact folder you want it to search and destroy that way it isn't indexing the whole root drive and take forever to run.

http://tlarkin.com/tech/search-and-destroy

As for the /users/Shared you could write a loop and use even parts of that script I posted

hayne 03-25-2009 10:11 AM

Quote:

Originally Posted by tlarkin (Post 525684)

It looks like your script will only delete (for example) ".mp3" files, but not ".MP3" files.
The OS X filesystem is case-preserving, but case-insensitive. However the case-insensitivity only means that if you ask it to delete a file "foo.mp3", it will work to delete the file "foo.MP3" - but your script doesn't ask that. It explicitly only searches for files with lowercase suffixes.
You could fix this by simply changing to use "-iregex" instead of "-regex".

tlarkin 03-25-2009 10:27 AM

Quote:

Originally Posted by hayne (Post 525689)
It looks like your script will only delete (for example) ".mp3" files, but not ".MP3" files.
The OS X filesystem is case-preserving, but case-insensitive. However the case-insensitivity only means that if you ask it to delete a file "foo.mp3", it will work to delete the file "foo.MP3" - but your script doesn't ask that. It explicitly only searches for files with lowercase suffixes.
You could fix this by simply changing to use "-iregex" instead of "-regex".

Oh cool I didn't know that. All I know is disk quotas in OS X Server don't always work as advertised so I have to go off and delete a bunch of non school work on our file shares.

I will make the change you suggested. Thanks.

hayne 03-25-2009 11:17 AM

Anytime you have a script like this, I would strongly suggest that you have another script that sets up a bunch of test cases and then runs your script to check that it works on those test cases. And then when you find a bug in your script (e.g. that it doesn't delete ".MP3" files, you add a new case in your test script so that all future versions of the script will be checked to ensure that they work for that case as well.

tlarkin 03-25-2009 11:38 AM

The only problem I found is that the find command with the -print option spams the console, so I typically take it off once I see that it works, and I typically also use the -print command before the -delete one to make sure it works.

I just did a few audits and I didn't see any all caps file extensions on the file shares, but that doesn't mean they don't exist. It just had never occurred to me.

kaptagat 03-25-2009 12:23 PM

I am trying a variation of the script I am using to delete files in the student's temporary folder which are older than a month which is :-

find "/Student's Temporary Folder/" \! -newermt '1 month ago' -delete

So to delete all the stuff in the shared folder except one folder called say, Adobe, I tried this :-

find /Users/Shared/ \! -path */Adobe/* -delete

but this deletes everything and comes back with the message :-
find: -delete: /Users/Shared/: relative path potentially not safe

I tried /Users/Shared/ \! -name */Adobe/* -delete
but this keeps the Adobe folder BUT deletes everything inside which I expected.

Why is it so hard to delete everything except a certain folder?

hayne 03-25-2009 03:02 PM

Quote:

Originally Posted by kaptagat (Post 525719)
find /Users/Shared/ \! -path */Adobe/* -delete

You need to protect the wildcards (*) from expansion by the shell by putting them inside single-quotes.
Try something like:
find /Users/Shared/ \! -path '*/Adobe/*' -print

Hal Itosis 03-25-2009 04:04 PM

Quote:

Originally Posted by kaptagat (Post 525719)
So to delete all the stuff in the shared folder except one folder called say, Adobe,

find -x /Users/Shared -not \( -name "Adobe" -prune \) -ls

kaptagat 03-26-2009 05:41 AM

Quote:

Originally Posted by Hal Itosis (Post 525766)
find -x /Users/Shared -not \( -name "Adobe" -prune \) -ls

Unfortunately this with the -delete option leaves the Adobe folder but deletes its contents that are files pertaining to Acrobat Professional.

When I use find /Users/Shared/ this comes back :-

/Users/Shared/
/Users/Shared//.dateFile.plist
/Users/Shared//.DS_Store
/Users/Shared//Adobe
/Users/Shared//Adobe/test.txt

Then when I try find /Users/Shared/ \! -path '*/Adobe/*' -print
This comes back :-
/Users/Shared/
/Users/Shared//.dateFile.plist
/Users/Shared//.DS_Store
/Users/Shared//Adobe

It still finds the Adobe folder but strangely not its contents.

One solution would be to make the Shared folder read only. Would this cause any unexpected problems?

jbc 03-26-2009 11:42 AM

Quote:

Originally Posted by kaptagat (Post 525864)
Then when I try find /Users/Shared/ \! -path '*/Adobe/*' -print
This comes back :-
/Users/Shared/
/Users/Shared//.dateFile.plist
/Users/Shared//.DS_Store
/Users/Shared//Adobe

It still finds the Adobe folder but strangely not its contents.

Try dropping the ending slash and it should skip the folder as well: '*/Adobe*'. For more precise control, you could try a form like:
Code:

find /Users/Shared \! -path '*/Users/Shared/Adobe*' -print

kaptagat 03-26-2009 12:16 PM

Thanks a million JBC, works a treat!

jbc 03-26-2009 12:42 PM

Cool. But looking at what I posted I realized that it will possibly skip some unwanted files: for example, a folder in /Users/Shared/ named "Adobe Reader" and its files would not be deleted.

I think the following is what you want:
Code:

find /Users/Shared \! \( -path '*/Users/Shared/Adobe/*' -or -path '*/Users/Shared/Adobe' \) -print
Basically it skips either a folder named precisely "Adobe" in /Users/Shared, or any file that is contained in a folder named precisely "Adobe" at that location. Good luck!

Hal Itosis 03-26-2009 02:29 PM

Quote:

Originally Posted by Hal Itosis (Post 525766)
find -x /Users/Shared -not \( -name "Adobe" -prune \) -ls

Quote:

Originally Posted by kaptagat (Post 525864)
Unfortunately this with the -delete option leaves the Adobe folder but deletes its contents that are files pertaining to Acrobat Professional.


Hmm, I wonder what you did wrong that time. ;)
[note: the folder must be exactly named Adobe, no spaces or control chars, etc.]

Or, are you saying -ls and -delete are finding different files on your Mac?

Hal Itosis 03-26-2009 02:58 PM

Skipping more than one folder:

find -xE /Users/Shared -not \( -regex '.*/(Adobe|Infected|Library)' -prune \) -ls

^ skips "Adobe" folder, and "Infected" folder (from post#1), and a "Library" folder.

Add more folders using the vertical bar |
i.e., the ERE "or" symbol (same char as a pipe).

If case-insensitive matching is desirable, use iregex instead.
If skipping folders whose name start with Adobe (etc.) is desirable, add .*

find -xE /Users/Shared -not \( -regex '.*/(Adobe|Infected|Library).*' -prune \) -ls

-HI-

tlarkin 03-26-2009 03:10 PM

Quote:

Originally Posted by Hal Itosis (Post 525954)
Skipping more than one folder:

find -xE /Users/Shared -not \( -regex '.*/(Adobe|Infected|Library)' -prune \) -ls

^ skips "Adobe" folder, and "Infected" folder (from post#1), and a "Library" folder.

Add more folders using the vertical bar |
i.e., the ERE "or" symbol (same char as a pipe).

If case-insensitive matching is desirable, use iregex instead.
If skipping folders whose name start with Adobe (etc.) is desirable, add .*

find -xE /Users/Shared -not \( -regex '.*/(Adobe|Infected|Library).*' -prune \) -ls

-HI-


On a side note I just learned that if your case sensitivity is off, it won't find the file. Hayne pointed out to me that if you use -iregex it will search both upper and lower cases. Just a FYI.

Hal Itosis 03-26-2009 03:29 PM

Quote:

Originally Posted by tlarkin (Post 525956)
On a side note I just learned that if your case sensitivity is off, it won't find the file. Hayne pointed out to me that if you use -iregex it will search both upper and lower cases. Just a FYI.

The subject of "case" never surfaced in that >original thread< several months back.
Otherwise, yes... had case been specified, iregex is surely what I would have recommended.
[and -- having read post #4 above -- my previous message already raised the issue, FYI. ;) ]

tlarkin 03-26-2009 03:33 PM

Quote:

Originally Posted by Hal Itosis (Post 525960)
The subject of "case" never surfaced in that >original thread< several months back.
Otherwise, yes... had case been specified, iregex is surely what I would have recommended.
[and -- having read post #4 above -- my previous message already raised the issue, FYI. ;) ]

yeah well it had never crossed my mind until recently, so I just wanted to point it out again simply as an FYI...

A lot of times I forget about little things like that

kaptagat 03-27-2009 07:32 AM

Thanks to everyone for all your replies and Hal Itosis for your command works well and it is very useful to exclude additional folders by using "|".

The "find" command is obviously very powerful but its syntax reminds me of Egyptian hieroglyphics.

I now want to use it to sweep the Student's temporary folder of applications as it should work better than "rm *.app" because this doesn't remove any applications inside folders.

So using this : find * -name '*.app' -prune -print returns this:-
Camino.app
untitled folder/Camino.app

But when I run (as root) find * -name '*.app' -prune -delete
it doesn't delete anything.

Any ideas why?

fracai 03-27-2009 09:42 AM

Are you sure it's not deleting anything? Or just not printing the result?

add -print before -delete and try it again.

kaptagat 03-27-2009 11:29 AM

No, none of the applications are being deleted.

The command appears to work perfectly but nothing gets deleted. If I add
-print before -delete, then this comes back :-

Camino.app/Contents/MacOS/components/talkback/Talkback.app
Camino.app
untitled folder/Camino.app/Contents/MacOS/components/talkback/Talkback.app
untitled folder/Camino.app

kaptagat 03-31-2009 11:10 AM

So no one can suggest a reason why the found applications are not deleted?

Thanks in Anticipation.

fracai 03-31-2009 11:35 AM

A permissions issue?

If you're running "find <path> <search options> -print -delete" and you're seeing the app paths printed out, those directories should be deleted, unless you don't have the proper permissions to delete them. Though in that case I'd expect to see an error.

hayne 03-31-2009 11:52 AM

I haven't looked into this, but does 'find's "-delete" work to delete non-empty directories?
I would have guessed not since 'rm' won't delete non-empty directories (without the "-r" flag).

Hal Itosis 03-31-2009 01:54 PM

Quote:

Originally Posted by kaptagat (Post 526060)
So using this : find * -name '*.app' -prune -print returns this:-
Camino.app
untitled folder/Camino.app

But when I run (as root) find * -name '*.app' -prune -delete
it doesn't delete anything.

Those two find commands (from post #20) are awkwardly written.

"find *" is neither conventional nor wise (imho).
Use a definite path (else, -f with more than one path).

And, "-prune" in the context of those two statements is going to do strange things.
[a few test runs show it skips apps which reside *inside* of other apps... weird.]

Anyway, i won't comment further until i see a proper search path and proper use of prune.
(in fact, -prune is probably not needed... unless you're trying to skip specific subregions).

--

i think hayne may be right about the behavior of -delete,
but the odd syntax above certainly doesn't help either.

EDIT: in fact, here is an interesting mix of conditions (from man find):
-d Cause find to perform a depth-first traversal, i.e., directories are visited in post-order and all entries in a directory will be acted on before the directory itself. By default, find visits directories in pre-order, i.e., before their contents. Note, the default is not a breadth-first traversal.

-depth Always true; same as the -d option.

-delete Delete found files and/or directories. Always returns true. This executes from the current working directory as find recurses down the tree. It will not attempt to delete a filename with a ``/'' character in its pathname relative to ``.'' for security reasons. Depth-first traversal processing is implied by this option.

-prune This primary always evaluates to true. It causes find to not descend into the current file. Note, the -prune primary has no effect if the -d option was specified.

So... -delete implies -depth (-d), and -prune doesn't perform its intended function under that condition?

idunno, but it looks like hayne's suggestion that using rm -r is what's needed (either pipe to xargs or use -exec).

I'm highly hesitant to actually *post* any working "rm -r" solutions however! :D

kaptagat 04-01-2009 06:11 AM

Hal Itosis

Thanks for your reply but there is no path because, for safety's sake, I am testing the command within the "Student's Temporary Folder". I am well aware of the danger of rm -r and I have no wish to wreck my machine.

Following your tip, I have looked into using -exec with find, and have come up with :-

find * -name '*.app' -exec rm -r {} \;

This command finds all applications and deletes them.

Hal Itosis 04-01-2009 02:28 PM

Quote:

Originally Posted by kaptagat (Post 526830)
Thanks for your reply but there is no path because, for safety's sake, I am testing the command within the "Student's Temporary Folder".

In that case, the proper syntax is: find .

While a few test runs show that "find *" seems to work, i wouldn't trust it.
I have never (never!) seen "find *" used in any script or webpage anywhere.
[unfortunately, google doesn't search for such strings successfully.]

But, we can see what's happening very easily... using execution tracing.

First, run this command in Terminal:
set -x
# for a conspicuously-colored trace prompt, also run: PS4=$'\[\e[37;41m\]+\[\e[0m\] '

Now... go to various folders and compare what happens when running:
find . -name '*.app'
versus:
find * -name '*.app'

The results may be identical, but you can see that very *different* arguments are being fed to find.
[and -- because of all those unnecessary arguments -- you might encounter some pitfall one day.]

When satisfied, turn off tracing via:
set +x

kaptagat 07-10-2009 07:13 AM

Still working on this for a neat solution without using the find command which is a complete minefield to a beginner. I am looking to use the inverse function of grep to find everything in the shared folder which is not called Adobe or Infected and then delete them.

I have got as far as this :-

rm -r `ls | grep -v "Adobe" | grep -v "Infected"`

This runs the ls grep command and then the rm command. I thought I had cracked it but bizarrely it doesn't delete any folders or files that have spaces in their names.

For example, untitledfolder gets removed but untitled folder comes back with :-
rm: untitled: No such file or directory
rm: folder: No such file or directory

Anybody know why?

thanks

fracai 07-10-2009 10:55 AM

rm -r `blah` is dangerous. Partly for the bug you've seen. Because "untitled folder" contains a space, it is interpreted as 2 arguments. A better solution along these lines would use xargs to tokenize your arguments and kick off the rm command.

ls | grep -v "Adobe" | gre p-v "Infected" | gxargs -d "\n" rm -r

Will pipe the ouput of your ls and grep into xargs, splitting the result by new lines, and executing rm on each argument.
Note that I'm using gxargs. This is because the xargs provided by Apple does not support the -d argument. So I'm using gxargs as provided by MacPorts. An alternative would be to use find and -print0.

find . \! -name "*Adobe*" \! -name "*Infected*" -print0 | xargs -0 rm -r

In this case, -print0 prints records separated by ASCII 0 and -0 tells xargs to look for this token. \! tells find to invert the following match, so it should exclude files with Adobe or Infected in the name.

kaptagat 07-14-2009 06:40 AM

Thanks fraicai but macports is not on the machines so gxargs is a non starter and I cannot get xargs to work and I don't want to use the find command. (Your find command finds files inside the Adobe folder and deletes them which is not what I want.)

If I pipe the output of the ls and grep commands to a file, I will get a list of everything which must be deleted with each file or folder being on a separate line. Is there a way for the rm command to open a file and execute the contents line by line?

thanks

P.S. Tried xargs rm < file.txt but came up against the space problem again.

hayne 07-14-2009 08:28 AM

Look at the following - it should help you to figure out a solution to your problem:
Code:

% cat foo.txt
/MyStuff/my first file.txt
/MyStuff/my second file.txt

% cat foo.txt | xargs echo
/MyStuff/my first file.txt /MyStuff/my second file.txt

% cat foo.txt | xargs -L 1 echo
/MyStuff/my first file.txt
/MyStuff/my second file.txt

% cat foo.txt | enquote
"/MyStuff/my first file.txt"
"/MyStuff/my second file.txt"

% cat foo.txt | enquote | xargs -L 1 echo
/MyStuff/my first file.txt
/MyStuff/my second file.txt

% cat foo.txt | enquote | xargs rm
rm: /MyStuff/my first file.txt: No such file or directory
rm: /MyStuff/my second file.txt: No such file or directory

The 'enquote' command is actually a Bash function:
Code:

# enquote: surround lines with quotes (useful in pipes) - from mervTormel
enquote () { /usr/bin/sed 's/^/"/;s/$/"/' ; }


fracai 07-14-2009 10:51 AM

Quote:

Originally Posted by kaptagat (Post 542569)
Your find command finds files inside the Adobe folder and deletes them which is not what I want.

Sorry, should have used path.

find . \! -path "*Adobe*" \! -path "*Infected*" -print
find . \! -path "*Adobe*" \! -path "*Infected*" -print0 | xargs -0 rm -r

kaptagat 07-15-2009 06:03 AM

Thanks fracai

I think I have finally got a robust command :-

find /Users/Shared/* \! -path "*/Users/Shared/Adobe*" \! -path "*/Users/Shared/Infected*" -print0 | xargs -0 rm -r

In testing, this keeps "Adobe" but deletes "Adobe folder", "erty adobe hrrkl", "adobe new folder" and "newadobefolder"

Hayne, I can see where you are going and it looks pretty neat but when I use "enquote", it comes back with bash command not found. I use 10.5.7 on a G5.

hayne 07-15-2009 11:00 AM

Quote:

Originally Posted by kaptagat (Post 542689)
when I use "enquote", it comes back with bash command not found

'enquote' is not a standard command - you need to add the line I provided above to your shell configuration files. Or just run the line I provided above in your Terminal window before running the other commands - that defines a Bash function 'enquote'.
Read the section on "shell aliases" in this Unix FAQ

tw 07-15-2009 11:06 AM

I know this is the unix section, but I feel I have to point out that this is really easy in applescript:
Code:

set sharedFolder to path to shared documents from user domain
tell application "Finder"
        set killThese to every item of folder sharedFolder whose name is not "Infected"
        delete killThese
        empty trash
end tell

if you want to skip more than one item, add an "and name is not <itemname>" to the third line for each item you want to save. you can run a similar script on the student's temporary folder; at first blush I'd try something like:
Code:

        set killThese to every item of folder sharedFolder whose kind does not contain "Microsoft" and kind does not contain "Quark" and kind does not contain "Photoshop"

tlarkin 07-15-2009 11:53 AM

find has a lot of over head as it searches your whole drive. When I remove things like programs I typically just set the full path as a variable and do a simple loop to check if that path is there, then if so delete it.

I had used the find command in the past, and yes it is useful but it killed my log in hooks and it killed my log files and caused some problems.

The find command is great when you don't know the path to said files, but if you know the path I say just use the full known path and not use the find command.

jbc 07-15-2009 03:25 PM

With regards to your most recent solution using the find command, I think the fastest and safest option might be something like the following:
Code:

find -E /Users/Shared -mindepth 1 -maxdepth 1 \! -iregex "^/Users/Shared/(Adobe|Infected|\.DS_Store)" -print0 | xargs -0 rm -r
Note that this does not descend into *any* folders within /Users/Shared since you're going to delete their contents (or not) with "rm - r" anyway. You can add anything else you decide to keep (including invisible files like ".DS_Store" as shown; escape any "." chars) by adding them to the name list separated by an OR operator ("|"): "(Adobe|Infected|\.DS_Store|Save This|Don't Trash)". You could use "-prune" here, but it seems redundant since you're already constraining find to looking only at the directory contents of the /User/Shared folder by using "-mindepth 1 -maxdepth 1".

kaptagat 07-16-2009 09:36 AM

Hayne

My script is below :-

#!/bin/sh

enquote () { /usr/bin/sed 's/^/"/;s/$/"/' ; }

cd /Users/Shared
ls | grep -v "Adobe" | grep -v "Infected" > foo.txt
cat foo.txt | enquote | xargs rm -r

This works perfectly when used with LoginWindow Manager so I think I will use it instead of the find command one because I am not really at home with that one.

Thanks to everyone for their replies and help.

tlarkin 07-16-2009 10:00 AM

Quote:

Originally Posted by kaptagat (Post 542901)
Hayne

My script is below :-

#!/bin/sh

enquote () { /usr/bin/sed 's/^/"/;s/$/"/' ; }

cd /Users/Shared
ls | grep -v "Adobe" | grep -v "Infected" > foo.txt
cat foo.txt | enquote | xargs rm -r

This works perfectly when used with LoginWindow Manager so I think I will use it instead of the find command one because I am not really at home with that one.

Thanks to everyone for their replies and help.

To be a bit safer you may want to use the pwd command to make sure it is in the proper folder before you execute any commands.


All times are GMT -5. The time now is 10:19 PM.

Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2014, vBulletin Solutions, Inc.
Site design © IDG Consumer & SMB; individuals retain copyright of their postings
but consent to the possible use of their material in other areas of IDG Consumer & SMB.