Never been to CodeSnippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world (or not, you can keep them private!)

get rid of mac's ._ meta files on unix command line (See related posts)

When a mac accesses storage not formatted in it's own HFS format, it stores it's additional metadata about the file in another file named the same but starting with "._" It can be annoying and when you're accessing a remote volume that's source control managed with subversion, they show up your status information. This bash shell script will get remove of all ._metadata files residing below the current working directory.

#!/bin/sh
for i in `find . -regex '.*\._.*'`; do rm $i; echo "removing $i"; done


If you save that as "/home/$USER/bin/cleanup" and do a "chmod 777 /home/$USER/bin/cleanup" it will be available next time you open a terminal by just typing "cleanup".

Comments on this post

Carloz posts on Jul 19, 2008 at 13:29
This will fail with filenames which include spaces and punctuation, generally. E.g. a directory containing two files "my song 1" and "my song 2" will
expand to:

for i in my song 1 my song 2 ; do .... ; done


Which will attempt to do things to files my, song, 1 and 2, none of which exist. It's not hard to imagine that file names with spaces might result in other files being deleted if their names happen to correspond in part.

It will also fall over with big directories, as the find command expands to an argument list for the for command, and may fairly easily wind up too long.

It could also be dangerous with certain file names e.g. if you were daft enough to have a file name containing the string ";\ rm\ -rf\ /", say.

You can partially solve the punctuation issue and fix the large directory problem using this type of approach instead:

#!/bin/bash

find . -regex '.*\._.*' | while read n ; do rm "$n" ; done


This pipes the output of the find operation, which may now be of arbitrary length, into the while loop, and n is assigned by the read command to the whole file name in each case. $n is placed in quotes so that when n is expanded, punctuation and spaces are protected from further shell interpretation.

This approach doesn't fail with single quotes in filenames (quite common in e.g. MP3 file names, as an apostrophe), but it doesn't defend you against file names with forward slashes in them, since the forward slash "escapes" the next character, even in quotes - so if you have a file named "file\one", the above will attempt to remove a file called "fileone" instead. To fix this, you need to escape the escape character:

find .  -regex '.*\._.*' | sed 's/\\/\\\\/g' | while read n ; do rm "$n" ; done


Note how you have to escape the escape character in the argument to sed.

There might be issues with other special-meaning characters I haven't thought of, too!
Carloz posts on Jul 19, 2008 at 13:38
Alternatively, incidentally, you could use the -exec function of find:
find . -regex '.*\._.*' -exec rm {} \; 

Which is a lot simpler and seems to deal with shell metacharacters in file names without any hassle.
Carloz posts on Jul 19, 2008 at 13:57
Oops, for forward slash read backslash - I always get the names mixed up, sorry!
Carloz posts on Jul 19, 2008 at 14:15
Final tip - find's -exec launches a separate process for each instance, which can be slow on long lists. Piping the output of find to xargs will launch one process per sizable chunk of args, and is loads quicker, but beware metacharacters again. Newer versions of find have an option, -print0, which terminates output lines with a zero, and xargs has an option, -0 which tells it to regard zero bytes, rather than spaces, as the separator. So you can do:
 find . -regex '.*\._.*' -print0 | xargs -0 rm

With my version of find and xargs (MacOS X 10.4), the above seems to deal with most nasty characters.
pbmods posts on Sep 08, 2008 at 13:40
I think the regex is a bit much when find's -name will do the job just fine:

for file in `find . -name ._*`; do echo "Removing $file"; rm $file; done;

You need to create an account or log in to post comments to this site.