The macosxhints Forums

The macosxhints Forums (http://hintsforums.macworld.com/index.php)
-   UNIX - Newcomers (http://hintsforums.macworld.com/forumdisplay.php?f=15)
-   -   removing the contents of a directory (http://hintsforums.macworld.com/showthread.php?t=90404)

LingaringBell 06-05-2008 02:40 AM

removing the contents of a directory
 
Is there a way in terminal to remove all the contents of a folder, but not the folder itself? If not, perhaps an easy way to do it in Applescript? Thanks.
-Bell

baf 06-05-2008 03:44 AM

Yes.


rm -r /path/to/folder/
don't forget that trailing slash.

LingaringBell 06-05-2008 04:06 AM

thanks baf, but your idea doesn't work, it still deletes the folder. However, it got me thinking and I believe I figured it out. Use this:

rm -r /path/to/folder/*

That seems to do the trick. Thanks again.

ganbustein 06-05-2008 04:40 AM

Quote:

Originally Posted by LingaringBell (Post 474369)
Is there a way in terminal to remove all the contents of a folder, but not the folder itself?

Code:

cd the/directory/in/question
rm -rf * .[^.] .??*

Be very careful when typing that. The combination of 'rm' and '*' on the same line, especially in the presence of '-rf', should make you at least as nervous as using both hands to work on high voltage equipment.

Because it's scary, I'll break it down and explain the pieces, on the theory that an incantation you understand is easier to type correctly:
  • rm remove the following items
  • -rf a combination of:
    • -r recursing into directories
    • -f forcing your way past easily-circumvented problems. (For example, if a non-empty subdirectory is non-writable, but you have permission to make it writable, just go ahead and do that without nagging you about it.)
  • * everything in the current directory whose name does not begin with a period
  • .[^.] and everything whose name is a period followed by a non-period
  • .??* and everything that starts with a period and is at least three characters long.

The net effect is to remove everything in the current directory except '.' (the directory itself) and '..' (its parent).

ganbustein 06-05-2008 04:48 AM

Quote:

Originally Posted by LingaringBell (Post 474376)
rm -r /path/to/folder/*

That doesn't remove files whose names begin with period. ('*' won't match those.) For example, it leaves behind .DS_Store, and things like .svn (subversion directories).

kaptagat 06-05-2008 06:36 AM

Can I ask a supplementary question to this topic please?

How would one delete all files in a folder that are older than say 3 months?

acme.mail.order 06-05-2008 09:16 AM

find /path/to/folder \! \( -newerct '3 months ago' \) -delete

Of course test it without the destructive option :D

kaptagat 06-05-2008 10:28 AM

Thanks acme but it doesn't seem to work. My unix is not so hot but do I really put '3 months ago' into the command?

baf 06-05-2008 12:45 PM

Yes it should work with that syntax. Try copying that line except -delete and replace path to folder with something else.

trevor 06-05-2008 12:47 PM

kaptagat, if it doesn't work the next time, please copy/paste from your Terminal the exact command you entered and the exact results, so we can see where it went wrong.

Trevor

Hal Itosis 06-05-2008 04:45 PM

Quote:

Originally Posted by ganbustein (Post 474379)
Code:

cd the/directory/in/question
rm -rf * .[^.] .??*

The net effect is to remove everything in the current directory except '.' (the directory itself) and '..' (its parent).

Quote:

Originally Posted by ganbustein (Post 474381)
That doesn't remove files whose names begin with period. ('*' won't match those.) For example, it leaves behind .DS_Store, and things like .svn (subversion directories).

Somewhere along the line, rm has been adjusted to compensate for the ".*" predicament.
Shell prints an error:
Code:

$ rm -i * .*
rm: "." and ".." may not be removed

But your regex " * .[^.] .??* " is still a smart idea.
(we can't always be sure the local rm has that particular feature)

-HI-

jsalmi 06-05-2008 05:33 PM

cd /path/to/folder
/bin/rm -rf * .*

acme.mail.order 06-05-2008 09:03 PM

Quote:

Originally Posted by kaptagat (Post 474422)
Thanks acme but it doesn't seem to work. My unix is not so hot but do I really put '3 months ago' into the command?

You do indeed. find will interpret almost any reasonable text string and change it into a date. You copied all the parentheses and backslashes, yes?

Hal Itosis 06-05-2008 09:45 PM

Quote:

Originally Posted by jsalmi (Post 474538)
cd /path/to/folder
/bin/rm -rf * .*

Right... but as mentioned above: on some systems (Panther maybe?),
that might also delete stuff in the *parent* folder. Just try it with ls to
see what could happen (on other systems):
$ cd ~/.Trash
$ ls * .*
ls: *: No such file or directory
.:

..:
Applications Documents Library Music Private Sites
Desktop Downloads Movies Pictures Public bin
:eek:
The shell's pattern matching facility will include stuff in the parent (..*)
ls just lists what the shell "asked" it to.

So, further design is required, to filter out all ..* matches that occur.
rm wasn't always so thoughtful to do that, as it appears to be today.

--

For example!!! :: DANGER :: Apple's own srm does not seem friendly in that regard!!!

Hal Itosis 06-05-2008 10:06 PM

Taking a cue from ganbustein,
this also seems to do the trick:
rm * .[^.]*

ganbustein 06-05-2008 10:43 PM

Quote:

Originally Posted by Hal Itosis (Post 474589)
Taking a cue from ganbustein,
this also seems to do the trick:
rm * .[^.]*

Nah. I made it as short as possible. Your pattern won't match and remove items named ... or ..secret_stash. And yes, I have seen such names used.

Hal Itosis 06-06-2008 01:37 PM

Excellent!

dexterbip 06-06-2008 03:19 PM

Interesting rm safety note: Keeping files called "-r" in directories can be a Bad Idea (not that I'm sure why you would have this file, or if there would be a situation where you'd think it'd be a good idea to use "rm *" in a directory to remove the top level files but keep everything lower down the directory tree).

Keeping a file called "-i" in a folder will definitely stop you rm-ing anything by mistake, but it's a pain in the backside if you do actually want to delete a bunch of stuff without confirmation.

Hal Itosis 06-06-2008 04:09 PM

Quote:

Originally Posted by dexterbip (Post 474744)
Interesting rm safety note: Keeping files called "-r" in directories can be a Bad Idea (not that I'm sure why you would have this file, or if there would be a situation where you'd think it'd be a good idea to use "rm *" in a directory to remove the top level files but keep everything lower down the directory tree).

Keeping a file called "-i" in a folder will definitely stop you rm-ing anything by mistake, but it's a pain in the backside if you do actually want to delete a bunch of stuff without confirmation.

I think the trick there is to use the folder's full pathname
especially when we're 'parked' in the directory at the time.

Wrapping ganbustein's magical patterns with some brace expansion syntax,
and applying a $PWD preamble, we could do...
ls -l "$PWD"/{*,.[^.],.??*}

Simply change ls -l to rm -r ...after a full backup! :D
[just kidding -- the listing looks perfectamundo]

--

Or hell... full path not needed to dust off "-i" files.
A basic ./ should do it:
rm -r ./{*,.[^.],.??*}

Hal Itosis 06-06-2008 04:40 PM

Quote:

Originally Posted by ganbustein (Post 474596)
Your pattern won't match and remove items named ... or ..secret_stash.

I just noticed something interesting.
Both "..." and "..secret_stash" are visible in Finder!
:rolleyes:

ganbustein 06-06-2008 07:58 PM

Quote:

Originally Posted by Hal Itosis (Post 474759)
I just noticed something interesting.
Both "..." and "..secret_stash" are visible in Finder!

That's bizarre!

As for dealing with -r and -i files... as man rm mentions, rm uses getopt() to parse its arguments, so the usual -- option works.
rm -rf -- * .[^.] .??*
Hmm.. without that, a file named -- might prevent an unpredictable subset of your files from being deleted.

Of course, prefixing with ./ works also on systems where rm does not look for --.

Back in the olden days when I was learning Unix, I seem to recall that ? would not match a period. My first attempt was
rm -rf * .? ..?* ...*
but ? doesn't work like that anymore (if it ever actually did). I just thought I'd mention that.

This thread seems to be accentuating that every program still has at least one bug, even if the program is a one-liner. Anyone but me remember IEFBR14? Of course you can google it, but do you remember it without looking it up? (I don't know if the fact that today is my 62nd birthday, and I've been programming since I was 19, makes that easier or harder to remember.)

Hal Itosis 06-07-2008 10:47 AM

Quote:

Originally Posted by ganbustein (Post 474789)
That's bizarre!

As for dealing with -r and -i files... as man rm mentions, rm uses getopt() to parse its arguments, so the usual -- option works.
rm -rf -- * .[^.] .??*
Hmm.. without that, a file named -- might prevent an unpredictable subset of your files from being deleted.

Oh yeah... I'd forgotten about "--"

--

I found we can also circumvent the "-i" effect by changing the order so that the
bare asterisk is not first. For example... using ls with the -f option (to force it
to list items in the order they were submitted):
Code:

$ ls -lanf * .[^.] .??*
342395 -rw-r--r--  1 501  501  0 Jun  6 13:18 bar
342388 -rw-r--r--  1 501  501  0 Jun  6 13:32 .*
342394 -rw-r--r--  1 501  501  0 Jun  6 13:24 .z
342389 -rw-r--r--  1 501  501  0 Jun  6 13:18 ...
342390 -rw-r--r--  1 501  501  0 Jun  6 13:23 ....
342391 -rw-r--r--  1 501  501  0 Jun  6 13:18 ..secret_stash
342392 -rw-r--r--  1 501  501  0 Jun  6 13:32 .\*
342393 -rw-r--r--  1 501  501  0 Jun  6 13:18 .dot

$ echo * .[^.] .??*
-i bar .* .z ... .... ..secret_stash .\* .dot

Since the early * picked up the "-i" first, ls saw it as an (inode) option.
So the -i file isn't shown.

Now, reordering...
Code:

$ echo .[^.] .??* *
.* .z ... .... ..secret_stash .\* .dot -i bar

$ ls -lanf  .[^.] .??* *
-rw-r--r--  1 501  501  0 Jun  6 13:32 .*
-rw-r--r--  1 501  501  0 Jun  6 13:24 .z
-rw-r--r--  1 501  501  0 Jun  6 13:18 ...
-rw-r--r--  1 501  501  0 Jun  6 13:23 ....
-rw-r--r--  1 501  501  0 Jun  6 13:18 ..secret_stash
-rw-r--r--  1 501  501  0 Jun  6 13:32 .\*
-rw-r--r--  1 501  501  0 Jun  6 13:18 .dot
-rw-r--r--  1 501  501  0 Jun  6 15:54 -i

-rw-r--r--  1 501  501  0 Jun  6 13:18 bar

(-f wasn't necessary; it merely helped to reflect the actual input, which echo also showed).

As can be seen, I've been playing around with some other weird filenames: .* and .\*

hayne 06-07-2008 11:13 AM

Quote:

Originally Posted by ganbustein (Post 474596)
I made it as short as possible.

I know you didn't mean it this way, but if we are going for short (not to mention easier to type), it'd be better to use the following to delete all files & folders in the current directory:

find . -delete

Code:

% echo "rm -rf * .[^.] .??*" | wc -c
      20

% echo "find . -delete" | wc -c
      15


Hal Itosis 06-07-2008 12:14 PM

That's weird... find . -delete
finds '.' but it doesn't delete '.'
How come (no error nothin')?

hayne 06-07-2008 04:12 PM

Quote:

Originally Posted by Hal Itosis (Post 474882)
That's weird... find . -delete
finds '.' but it doesn't delete '.'
How come (no error nothin')?

It's merely that 'delete' is smart enough to know that it wouldn't make sense to delete the current directory. So it makes an exception for '.' and '..' - here's the relevant part of the source code:
Code:

        if (strcmp(entry->fts_accpath, ".") == 0 ||
            strcmp(entry->fts_accpath, "..") == 0)
                return 1;


Hal Itosis 06-07-2008 05:00 PM

:D :D :D Thread Closed! :D :D :D

No Further Information Necessary!

^^^

Well, there's still something to be said for the pattern approach:
it can be done from *outside* the folder. (I've already added a
Bash function to implement it... so it needs to be rationalized).

rm -fr foo/{.[^.],.??*,*}

will clear foo's content, but folder foo survives.
;)

ganbustein 06-07-2008 09:31 PM

Quote:

Originally Posted by hayne (Post 474872)
I know you didn't mean it this way, but if we are going for short (not to mention easier to type), it'd be better to use the following to delete all files & folders in the current directory:

find . -delete

It's true that I didn't mean it that way, but your version is not only shorter and easier to remember/type/understand, it's also more robust.

Code:


mkdir foo
cd foo
ls -a
.      ..
for (( i=0; i < 10000; ++i )); do touch someverylongfilenameprefix.$i; done
# very long wait...
rm *
-bash: /bin/rm: Argument list too long
sysctl kern.argmax
kern.argmax = 262144
# Aha! 10000 filenames at 27+ characters each exceeds kern.argmax
find . -delete
# very short wait...
ls -a
.      ..

The difference is that rm * forces bash to expand the complete list of files before handing it to rm, and the list is too long for IPC to handle. With find, -delete deletes each item immediately. No list needs to be created. I yield to the superior solution.

Hal: here's a start on your function:
Code:

rmstar ()
{
    local oldpwd="$PWD";
    cd "${1:-$oldpwd}" && find . -delete;
    local result=$?;
    cd "$oldpwd";
    return $result
}

I found that I kept getting errors out of find when I tried find "$1" -delete or variations thereof. For one thing, while find . -delete won't find and delete '.', find foo -delete will happily find and delete 'foo'. Say what? And $1 might or might not have a trailing slash. And after that there are warnings about relative pathnames, at which point I threw in the towel and just did a cd.

Add whatever additional error-checking code you deem appropriate. A nice refinement might be to loop across "$@" instead of just using "$1". Or have it accept files as well as directories.

Or just ask yourself how often you're really going to need this. After all:
rm -rf someDirectory
mkdir someDirectory
will almost always be good enough.

Hal Itosis 06-08-2008 02:24 AM

Quote:

Originally Posted by ganbustein (Post 474941)
Or just ask yourself how often you're really going to need this. After all:
rm -rf someDirectory
mkdir someDirectory
will almost always be good enough.

[shhhh... we don't want folks to think this stuff can be that simple.] ;)

Just Fred 09-25-2008 11:34 PM

Can I ask to go on another tangent? :)

I would like to be able to remove all the thumbnail files iPhoto creates. In each "primary" folder hierarchy ~/Pictures/iPhoto\ Library/<year>/<month>/ there can be a "Thumbs" directory containing these thumbnail files. I've been trying to use a combination of 'find' (to find the "Thumbs" directory and 'xargs' to delete the contents of these folders, but I can't seem to get it to work. As usual, I'm sure I'm overlooking something quite simple...

TIA

acme.mail.order 09-25-2008 11:47 PM

This one is very aggressive, the Four Horsemen of `find` annihilating entire directory trees at a stroke:

find . -iname '*thumb*' -exec rm -rf {} \;

Only two horses:

find . -type d -name thumb -exec rm {}/*jpg \;

Just Fred 09-26-2008 08:49 AM

Quote:

Originally Posted by acme.mail.order (Post 495522)
This one is very aggressive, the Four Horsemen of `find` annihilating entire directory trees at a stroke:

find . -iname '*thumb*' -exec rm -rf {} \;

Only two horses:

find . -type d -name thumb -exec rm {}/*jpg \;

Hmmm, I thought I'd tried those combinations, without success. It looks like the second would do the trick. I'd change it to

find . -type d -name Thumbs -exec rm {}/*jpg \;

Simply to guard against the possibility (however unlikely) that there might be a folder with "thumb" somehow in its name.

Thanks!


All times are GMT -5. The time now is 10:13 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.