Submit Hint Search The Forums LinksStatsPollsHeadlinesRSS
14,000 hints and counting!

Create aliases to icons via a Perl script UNIX
Often I read the question "Where are all the icons on my Mac?" Here is the answer, in the form of a Perl script. Just copy and paste the below code into a new file, save it as something (findOSXicons) to a directory in your path, and make it executable (chmod 755 findOSXicons). Then use the usual techniques to start the script (i.e. calling it using the full or relative path). Here's the code:

#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
use Pod::Usage;
use File::Find;
use File::Basename;

# here we store the results
my $target=$ENV{'HOME'} . '/Desktop/OSX-Icons';

# you may change the extensions to use
my @extensions= qw( icns );

###### POD #################################################################{{{

=head1 NAME

findOSXicons - recursively search for all icns files in your system

=head1 SYNOPSIS

B<findOSXicons>
[B<-target> I<targetdirectory>]
[B<-extension> I<ext>]...
[F<startpath>...]

B<findOSXicons>
B<-help>

B<findOSXicons>
B<-man>

=head1 DESCRIPTION

B<findOSXicons> recursively traverses the startpaths (or / if none given)
and creates a directory structure containing links to each and every icon
(files ending with .icns) found.

=head1 EXAMPLE

findOSXicons -target ~/Desktop/MyIcons -extension gif -extension icns ~

=head1 OPTIONS

=over 5

=item B<-extension> I<ext>

As a default the extension I<icns> is searched. with this option you
can specify extensions to use instead. If you give this option several
times, each extension is searched

=item B<-target> I<targetdirectory>

As a default the directory OSX-Icons is created on your desktop. With this
option you can specify which output directory to create instead.

=item B<-help>

Short help

=item B<-man>

Long help

=back

=head1 BUGS

None yet except that you get several warnings for directories you are not
allowed to acces. Use 2E<gt>/dev/null to suppress them.

=head1 ACKNOLEDGEMENTS

findOSXicons was entirely written by Skeeve
(findOSXiconsZ<>.Z<>questionsZ<>.Z<>skeeveZ<>@Z<>xoxyZ<>.Z<>net)

It was inspired by several user requests in the german apple forum at
L<http://www.apfeltalk.de/forum>.

=cut

###### END: POD ############################################################}}}

##### commandline options ##################################################{{{
# defaults
&help unless GetOptions(
	"help"        => \&help,
	"man"         => \&man,
	'target=s'    => \$target,
	'extension=s' => \@extensions,
);
sub help { exit pod2usage( verbose => 1 ); };
sub man { exit pod2usage( verbose => 2 ); };
##### end commandline options ##############################################}}}


# append a trailing slash
$target=~ s#([^/])$#/#;
# and calculate the length
my $target_length= length($target);

# check which extensions to find
die "No extension specified\n" unless scalar @extensions;
my $extensions= join ('|',map quotemeta,@extensions);
$extensions= qr/(\.(?:$extensions))$/;


# Statistics etc.
my @removed;        # list of removed icons
my $count_old= 0;   # icons we know of
my $count_new= 0;   # new icons
my $count_oapp= 0;  # application icons we know of
my $count_napp= 0;  # new application icons

# create the target directory unless it exists
mkdir $target or die "Can't create $target: $!\n" unless -d $target;

# hash of files already linked
my %stat;
# build the hash.
find ( {
	wanted => sub {
		# get the link target
		my $lnk= readlink $File::Find::name;
		# No link target? Then skip this.
		return unless defined $lnk;
		# Does the target exist?
		if ( -e $lnk ) {
			# build the hash
			++$stat{$lnk};
			# do some statistics
			++$count_old;
			# is it an applicationicon?
			++$count_oapp if $lnk=~ m#/[^/]+\.app/#;
		}
		else {
			# target is missing so remove the link
			unlink $File::Find::name;
			# and remeber that it was removed
			push(@removed, $lnk);
		}
	},
	no_chdir => 1,
}, $target);


# now find
find( {
	wanted => sub {
		# just files
		return unless -f $File::Find::name;
		
		# need to get the basename (no_chdir!)
		my $fname= basename $_;
		
		# correct extension?
		return unless $fname=~ s#$extensions##;
		my $ext= $1;
		
		# we don't want our target to be checked
		return if substr($File::Find::dir, 0, $target_length) eq $target;
		
		# check whether or not we already know of this file
		return if $stat{$File::Find::name}++;
		++$count_new;
		
		# create the subdir in the target dir
		my $link_dir= $target;
		# 2 possibilities
		if ( m#/([^/]+)\.app/# ) { # is it an Application's icon?
			# we put it into the Applications-subdirectory
			$link_dir.= 'Applications';
			mkdir $link_dir or die "Can't create $link_dir: $!\n" unless -d $link_dir;
			# into a directory for the application
			$link_dir.= "/$1";
			++$count_napp;
		}
		else {
			# we put it into an "alphabetical" subdirectory
			$link_dir.=  uc substr($fname,0,1);
		}
		# create the directory
		mkdir $link_dir or die "Can't create $link_dir: $!\n" unless -d $link_dir;
		
		# now calculate the filename
		my $link_name= $link_dir . '/' . $fname;
		my $count= '';
		--$count while( -e "$link_name$count$ext");
		# now link;
		symlink $File::Find::name, "$link_name$count$ext";
	},
	no_chdir => 1,
	follow => 0,
}, scalar @ARGV ? @ARGV : '/');

# Statistics
my $icon_count= $count_old + $count_new;
my $app_count= $count_oapp + $count_napp;
my $else_count= $icon_count-$app_count;
my $else_new= $count_new - $count_napp;
my $else_old= $count_old - $count_oapp;
my $miss_count= scalar @removed || 'none';
print <<REPORT;
      All icons known: $icon_count
    Application icons: $app_count
Non-application icons: $else_count
REPORT

print <<REPORT if $count_new;

      New icons found: $count_new
    Application icons: $count_napp
Non-application icons: $else_new

            Old icons: $count_old
    Application icons: $count_oapp
Non-application icons: $else_old
REPORT

print <<REPORT;

Icons removed since last run: $miss_count
REPORT

print "$_\n" foreach (sort @removed);
By default, this script will copy a link (kind of an alias) to each and every icon in the system into a folder on the desktop in a nice hierarchy.

Each icon belonging to an Application will be linked from a folder:

Desktop » OSX-Icons » Applications » application_name » icon_name

Each icon not belonging to an application wil be linked from a folder:

Desktop » OSX-Icons » letter » iconname

where letter is the first character of the icon's name. But you can do more with it! Instead of just starting findOSXicons from the Terminal, you can also tell it to:
  • search for other file extensions
  • store the results elsewhere
  • don't search the whole system
Just enter findOSXicons -help or findOSXicons -man for instructions.

[robg adds: I tested this, and it works ... but it can take a *long* time to run if you've got a ton of files on your machine. If you'd rather not copy, paste, and make executable, I've put a zipped ready-to-use version on the macosxhints server.]
    •    
  • Currently 1.20 / 5
  • 1
  • 2
  • 3
  • 4
  • 5
  (5 votes cast)
 
[10,110 views]  

Create aliases to icons via a Perl script | 18 comments | Create New Account
Click here to return to the 'Create aliases to icons via a Perl script' hint
The following comments are owned by whoever posted them. This site is not responsible for what they say.
Create aliases to icons via a Perl script
Authored by: pietro on Jun 24, '08 08:29:54AM

I get a syntax error "findOSXicons.pl:227: Global symbol "$_n" requires explicit package name". Shouldn't this be "$_\n" and not "$_n" ?



[ Reply to This | # ]
Create aliases to icons via a Perl script
Authored by: leamanc on Jun 24, '08 08:54:55AM

More than likely, yes. I didn't read the script, but I can say from past experience that Geeklog strips out backslashes all the time on Mac OS X Hints.



[ Reply to This | # ]
I get a syntax error "findOSXicons.pl:227:
Authored by: simbalala on Jun 24, '08 09:21:50AM

Me too



[ Reply to This | # ]
Create aliases to icons via a Perl script
Authored by: simbalala on Jun 24, '08 09:27:36AM

OK, I got a little further this time. Where else might there be missing backslashes?

And when Rob says he post a zipped file? Where?



[ Reply to This | # ]
Create aliases to icons via a Perl script
Authored by: robg on Jun 24, '08 09:36:06AM

Ever have one of those mornings? The zip file is now linked (and the backslashes are fixed).

-rob.



[ Reply to This | # ]
OK, the zipped one runs...
Authored by: simbalala on Jun 24, '08 09:48:26AM

using sudo.



[ Reply to This | # ]
you don't need sudo
Authored by: Skeeve on Jun 24, '08 12:56:23PM

just ignore the warnings of directories you may not view.



[ Reply to This | # ]
Create aliases to icons via a Perl script
Authored by: Skeeve on Jun 24, '08 09:26:55AM

Yes. All the backslashes are gone which makes this script almost unusable. I'll write rob.



[ Reply to This | # ]
Create aliases to icons via a Perl script
Authored by: robg on Jun 24, '08 09:33:09AM
Sorry about that ... it's fixed now (and the downloadable zip should be fine).

-rob.

[ Reply to This | # ]
Create aliases to icons via a Perl script
Authored by: simbalala on Jun 24, '08 10:09:53AM
This is a case where ImageBrowser comes in real handy.

[ Reply to This | # ]
CoverFlow
Authored by: S on Jun 24, '08 11:37:51AM

Could you make a version to actually duplicate the respective files? One of the big disadvantages of aliases at the moment is that they currently don't work in CoverFlow.



[ Reply to This | # ]
sure I can, but...
Authored by: Skeeve on Jun 24, '08 12:54:36PM

The problem when copying is, that I can't find whether or not an icon is already copied.

Solutions I can offer:
a) create the structure as is, but at the same moment create an additional structure containing copies.
b) Yo may use this script to create a copy of the structur containing a real copy of the files:
mkdir copy ; ( cd OSX-Icons ; tar cBpfh - . ) | ( cd copy ; tar xvBpf - )
start it in the folder where "OSX-Icons" (the result directory) resides. You'll get a directory "copy" containing the same structure



[ Reply to This | # ]
Create aliases to icons via a Perl script
Authored by: Fairly on Jun 24, '08 03:56:57PM
Wow. 227 lines of code? Perl code at that? Perl the most cryptic and spartanic of all scripting languages? You can make an entire OS in 227 lines of Perl. But the task at hand could be done a lot better with ordinary shell scripting I think. Or you could just use a utility like this.

http://rixstep.com/xscan

Coupled with this.

http://rixstep.com/xshelf

It's finished before you know it. My Leopard box has three Xcode SDKs so it's got a lot of files and directories. It took less than three minutes to find over 1,800 ICNS files and transfer them to a shelf. And if I save the shelf file I get a single file added to the system. Everything - the 1,800+ "aliases" - are all in one file.

You're making too much of a project out of this. It's commendable but it's simply too much work!

[ Reply to This | # ]
Too much code?
Authored by: Skeeve on Jun 24, '08 11:53:28PM

> Perl the most cryptic and spartanic of all scripting languages?
Cite: "Wenn man keine Ahnung hat: Einfach mal Fresse halten"" Dieter Nuhr
(Translation: "If you don't have a clue, just keep your trap shut.")

Of course this is just 95 lines of Code (uncompressed and indented for legibility). The rest is documentation and explanation.

> But the task at hand could be done a lot better with ordinary shell scripting I think.

In which sense of "better". "Better documented"? "Better understandable"? I don't think so! No! I'm sure not! Hey! I'm not here to win a perl golf contest, I'm here to give the people something they can use and, if they want to, learn from.

> It's finished before you know it. My Leopard box has three Xcode
> SDKs so it's got a lot of files and directories. It took less than three
> minutes to find over 1,800 ICNS files and transfer them to a shelf.
> And if I save the shelf file I get a single file added to the system.
> Everything - the 1,800+ "aliases" - are all in one file.

You're such a hero! BTW: May I ask how many lines of code those 2 programs need? And does it give you the same structured result?

> You're making too much of a project out of this.

Maybe. But that's
a) none of your business
b) a script I (and hopefully others) will still understand 2 years from now






[ Reply to This | # ]
Spotlight?
Authored by: jediknil on Jun 24, '08 08:28:52PM

Um, if you have Tiger or Leopard, Spotlight can do this sort of thing for you with a Saved Search. It's a little harder to filter out the Applications, but it's possible, and it'll auto-update. (As of Leopard Spotlight is a lot better, too; I actually use it now.)



[ Reply to This | # ]
Spotlight?
Authored by: S on Jun 25, '08 03:21:07AM

How do you get Spotlight to search inside bundles?



[ Reply to This | # ]
Spotlight?
Authored by: lar3ry on Jun 25, '08 07:40:40AM

Actually, using the "locate" database (if you are running updatedb on a regular basis) would probably be a good compromise of using a brute-force "find" (even Perl's Find package) and Spotlight (with its inherent limitations).

"locate .icns" should probably run faster than the Perl Find, albeit maybe not quite as fast as a true Spotlight search, since both locate and Spotlight simply query a database instead of traversing every file on every mounted volume in real time. (The locate database is created regularly by updatedb, but this may or may not be enabled in a typical OS X install; it's easy enough to enable, though.)

I leave the mechanism of using locate instead of Perl Find as an exercise for the reader.



[ Reply to This | # ]
Create aliases to icons via a Perl script
Authored by: spockboy on Jun 25, '08 03:25:28PM
I like one-liners:
locate '*.icns'
From there, you can get fancy:
locate '*.icns' |xargs cp /home/foo/


[ Reply to This | # ]