fileicon: setting custom icons on macOS from a script
macOS CLI for managing custom icons for files and folders
At a glance
- What is it?
- fileicon is a macOS shell utility that assigns, removes, extracts and tests custom icons for files and folders, which is the part of Finder that has no scriptable interface. The icon is written into the resource fork, a hidden file or a FinderInfo attribute, which is why it cannot travel with a git repository.
- Who is it for?
- fileicon suits a macOS user who wants to script icon assignment, since the four sub-commands cover setting, removing, extracting and testing in a form that drops into a shell loop, and the test sub-command answers a question the filesystem listing cannot.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Four sub-commands for a job Finder refuses to script
fileicon is a macOS command line utility for managing custom icons for files and folders, and the positioning is explicit: it is a programmatic alternative to interacting with Finder by hand.
The whole API is four verbs, and the built-in help prints them in a fixed shape:
$ fileicon --help
fileicon set <fileOrFolder> [<imageFile>]
fileicon rm <fileOrFolder>
fileicon get [-f] <fileOrFolder> [<iconOutputFile>]
-f ... force replacement of existing output file
fileicon test <fileOrFolder>
All forms: option -q silences status output.
Standard options: --help, --man, --version, --homeSetting a custom icon takes a target and an optional image file, removing one takes just the target, extracting one takes a target and an optional output file with a force flag for replacing an existing output, and testing takes a target and answers whether it has a custom icon. Every form accepts a quiet option that silences the status output, and the standard options are help, man, version and home.
The examples in the documentation are four one-liners, and they show the shape of the tool better than a description would:
# Assign custom icon derived from image file 'img.png' to file 'foo':
fileicon set foo img.png
# Remove previously assigned custom icon from folder 'foodir':
fileicon rm foodir
# Extract custom icon from file 'foo' to icon file 'foo.icns':
fileicon get foo
# Test if folder 'foodir' has custom icon:
fileicon test foodirThe test sub-command is the one worth noticing. It answers a question that has no other good answer on the command line, because the state you are asking about is an extended attribute on the file rather than anything visible in a directory listing, and asking whether a file has an icon is a natural thing for a script to need to know.
The extract sub-command is the other one. Pulling an icon back out of a file turns the tool from a writer into a round trip, which is what makes it usable for inspecting or for moving a custom icon between files.
The primary language recorded for the repository is Shell, and the package manifest points at bin/fileicon as the single binary, with the man page at man/fileicon.1.
The icon ends up in three places at once
The mechanism is the part of this project that is genuinely instructive, and it is documented in detail.
When you assign an image with fileicon set, a set of icons in several resolutions is created and stored in the resource fork of the target file itself. For a folder, the icon is stored instead as the content of a hidden file called Icon with a carriage return in the name, inside the target folder. For folders that are volume mountpoints, it is the content of a hidden .VolumeIcon.icns file. Additionally, a com.apple.FinderInfo extended attribute carrying the custom-icon attribute, which is a flag, is set on the target file or folder itself.
Three places, then. The FinderInfo attribute is what makes Finder display the icon, the resource fork or the hidden file holds the actual artwork, and the two have to agree for the icon to appear and to survive a copy.
The resolution detail follows from that: the icon with the highest resolution measures 512 by 512 pixels, and the input image is scaled accordingly. So you are not restricted to handing over an exact size, the tool does the scaling, and the ceiling is what the system will display.
There is a caveat about volumes, and it is unusually candid. Version 0.3.2 and later supports custom icons for volumes, which are folders that act as volume mountpoints, in principle, but as of macOS 13.1 Ventura this often fails in practice, for reasons the author says are unknown. A linked discussion on the Ask Different site is given as an example.
The other caveat is about symlinks. If you supply a path to a symlink, it is invariably the target that is used for the operation, because symlinks themselves cannot have icons associated with them.
Extended attributes, and why an icon cannot live in a git repository
The consequences of the storage mechanism are spelled out in the caveats section, and one of them is a constraint that shapes how the tool can be used.
Custom icons rely on extended attributes of the macOS filesystems, HFS+ and APFS. Therefore custom icons are lost when copying files or folders to filesystems that do not support these attributes, and the example given is that custom icons cannot be stored in a Git repository.
That is not a limitation of fileicon so much as a property of the platform, and it is worth internalising before you build a workflow on it. The icon lives in the filesystem metadata, so anything that moves the file rather than the filesystem, a git checkout, a file transfer to a non-APFS target, an archive that does not preserve resource forks, loses it.
The consequence for scripts is that an icon is not part of the file's identity. Two copies of the same file can look different in Finder, and the tool's test sub-command is the only reliable way to ask which is which.
There is a related limitation in the get sub-command that has nothing to do with icons. A bug beyond the control of the tool causes the get sub-command, for extracting an existing icon, to be excessively slow on macOS 12 Monterey. That was fixed in macOS 13 Ventura, so the slow path affects the version of macOS that many people were on when this was written, and not the current one.
The other platform note is about Python. If you are running macOS 12 Monterey or higher, be sure to install at least version 0.3.1 of the utility, as it no longer relies on Python, which no longer ships with macOS. That is the reason the manual installation route exists at all, since a script that needs a language the operating system stopped shipping is a script that breaks on upgrade.
Three installation routes, and a manifest that names one binary
Only macOS is supported, and there are three ways to get the tool onto a machine.
Homebrew is the shortest route:
brew install fileiconThe formula was created and submitted by another developer, and the documentation credits them for it. That is a small detail but it tells you the tool is a normal member of the Homebrew ecosystem rather than something you are expected to build yourself.
The npm registry route is the second, and it is the one that explains the repository layout:
[sudo] npm install fileicon -gTwo notes come with it. Whether you need sudo depends on how you installed Node.js and whether you changed permissions later, so if you get an EACCES error you try again with sudo. And the global flag is needed to put fileicon in the system's PATH.
The manual route is three steps. Download the CLI as fileicon, make it executable with chmod +x, and move or symlink it into a folder in your PATH such as /usr/local/bin, which requires sudo.
The package manifest explains the npm story. The package is named fileicon, version 0.3.5, it is not private, it declares an os of darwin only, and preferGlobal is set so that npm installs it globally by default. The bin entry maps the command name fileicon to bin/fileicon, and the man field points at man/fileicon.1.
The dependency list is all development-time. doctoc for the table of contents, json, marked and marked-man for rendering, replace for text substitution, semver and urchin for release tooling. There are no run-time dependencies at all, which is consistent with the tool being a single script with no runtime.
The top level of the repository matches: a bin/ directory, a doc/ directory, a man/ directory, a test/ directory, a Makefile, a CHANGELOG.md, a LICENSE.md, a TODO.md and a package-lock.json.
The readme is generated, and that is deliberate
Several sections of the documentation carry a comment saying not to edit them, and following those instructions tells you how the project is organised.
The table of contents is generated by doctoc, and the comment says not to edit that section but to re-run doctoc instead. The fenced code block holding the CLI usage is updated by make update-readme/release with the CLI usage information. The licence chapter is updated from the contents of LICENSE.md, and the dependencies chapter is updated from the dependencies in package.json.
So the README.md you read on the repository page is a build artefact assembled from several sources: the hand-written prose, the usage output of the tool itself, the licence file and the package manifest. That arrangement keeps the two things most likely to drift, the help text and the dependency list, mechanically in step with the code.
It also explains the presence of the Makefile, the doc/ directory and the man/ directory. The manual lives at doc/fileicon.md and is also rendered as a man page, and the documentation tells you to read that manual for the complete documentation or to run man fileicon once installed, with fileicon --man if you installed manually.
The licence is MIT, and the copyright line reads 2015 to 2026 with the author's name and email. A contributor is credited in the manifest alongside the author.
The last push was on 2026-10-18 and there are no GitHub releases, so the release history lives in the CHANGELOG.md rather than in tags, and the npm version badge is the one to watch if you want to know what is current.
For a tool this small the engineering is unremarkable, but the care in the documentation pipeline is not. A CLI whose help text is generated from the CLI cannot drift from the CLI, and the acknowledgements section spells out the dependency suffix convention, where D marks a development-time-only dependency, O an optional one and P a peer dependency, with no suffix meaning a required run-time dependency.
Editorial conclusion
fileicon suits a macOS user who wants to script icon assignment, since the four sub-commands cover setting, removing, extracting and testing in a form that drops into a shell loop, and the test sub-command answers a question the filesystem listing cannot. It does not suit you if you expect an icon to travel with the file, because the icon lives in filesystem metadata and is lost on any copy to a filesystem without extended attributes, and it does not suit you on anything other than macOS. Before you rely on it, check the version you have, since 0.3.1 is the floor on macOS 12 and later because the tool no longer relies on Python, and note that setting an icon on a volume mountpoint often fails on macOS 13.1 for reasons the author could not determine.
Frequently asked questions
What does the fileicon command do on macOS?
It manages custom icons for files and folders through four sub-commands: set assigns an icon from an image file, rm removes one, get extracts an existing icon to an output file, and test reports whether a file or folder has a custom icon. Every form accepts a quiet option to silence status output.
How do I install fileicon?
Run brew install fileicon with Homebrew, or npm install fileicon -g with Node.js, which needs the global flag to place the command on your PATH and may need sudo depending on how Node.js was installed. For a manual install, download the CLI as fileicon, make it executable with chmod +x and move or symlink it into a folder on your PATH.
Why can a custom file icon not be stored in a git repository?
Custom icons rely on extended attributes of the macOS filesystems, HFS+ and APFS, and they are lost when files are copied to filesystems that do not support those attributes, which includes a git repository. The icon lives in filesystem metadata rather than in the file content.
What happens if I run fileicon on a symlink?
It is invariably the target of the symlink that is used for the operation, because symlinks themselves cannot have icons associated with them.
Which macOS versions are supported for fileicon?
macOS only. On macOS 12 Monterey or higher you need at least version 0.3.1, because the utility no longer relies on Python, which no longer ships with macOS. The get sub-command was also excessively slow on macOS 12 because of a bug outside the tool's control, and that was fixed in macOS 13 Ventura.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mklement0-fileicon)Was this useful?