Replaces BSD utilities with portable GNU utilities
A comprehensive collection of GNU/Linux utilities that replaces MacOS/BSD utilities with their GNU equivalents, so that commands in MacOS (dev machines) will have the same behavior as in Linux (Docker containers). This ensures portability and correctness of shell scripts and Makefiles.
📋 Note: When copying this repo to your project's
scripts/linuxify/directory, usersync -av --exclude='.git'to avoid git-within-git problems. This ensures clean project integration without nested git repos. See the Installation section below for more details.
All Linux distros (like those we use for Docker containers) use common utilities that're standardized and portable, such that the same command works the same way on any Linux machine. Perversely, MacOS has its own "special" versions of common utilities, and these BSD utilities are sometimes very different from their GNU/Linux counterparts. Many common utilities -- sed, tar, grep, even very basic commands like cp and mv -- differ enough that an invocation that works in Linux will fail in MacOS or vice versa -- or worse yet, an invocation will "succeed" in both but have different results! A classic example is a correct grep match in GNU/Linux grep can give a false negative with BSD grep.
Here's a concrete example showing how sed behaves differently:
# In-place file editing with BSD sed (macOS default):
sed -i '' 's/foo/bar/' file.txt # Works on BSD, fails on GNU
# In-place file editing with GNU sed (Linux):
sed -i 's/foo/bar/' file.txt # Works on GNU, fails on BSD
# With linuxify, macOS uses GNU sed, so scripts work consistentlyThis incompatibility means a script that works perfectly in your Docker container can silently fail on your Mac, or vice versa. linuxify solves this by ensuring both environments use the same GNU tools.
linuxify transparently replaces macOS-specific (BSD) utilities with GNU/Linux equivalents by:
- Installing a list of 40+ common system GNU programs
- Installing any that're missing & updating any that're outdated
- Superseding BSD programs with their preferred GNU implementation
This helps ensure shell scripts and Makefiles behave consistently between macOS development and Linux production environments.
linuxify is designed to have lower precedence than asdf. Our usual pattern is to manage system utilities (like grep and git) via linuxify and development tools (like python and node) via asdf, but there can be cases where that distinction isn't clear or correct. Since asdf's spec file .tool-versions is more often tailored to a project than the linuxify configuration is, asdf "wins" in cases of overlap.
🔗 Note on
asdf-direnvintegration: This repo works seamlessly with theasdf-direnvmix-in for automatic project-specific tool management. When used together,asdf-direnvautomatically sources thelinuxifyconfiguration without manual.envrcsetup. See the asdf-direnv integration section below for details.
linuxify can be installed in a project-specific way (that exposes utilities and versions required by that particular project), and/or in a machine-wide way (that exposes common versions any time you're in a directory that doesn't have a project-specific linuxify). It's a good practice to use both modes: per-project to ensure your repos are portable, and machine-wide for other everyday non-"project" use.
For global system-wide GNU utilities integration:
- Location:
~/linuxify/ - Integration: Some adds to
~/.zshrcand~/.bashrc - Scope: Anywhere a project-specific config doesn't exist
- Activation: Every time a terminal session is created
- Configuration: Copies
.linuxifyto~/.linuxify - Auto-updates: Supports git-based updates
For project-specific integration within repositories:
- Location:
/PATH/TO/REPO/scripts/linuxify/ - Integration: Works with project's
.envrcviadirenv - Scope: Only within that particular project directory
- Activation: Automatic when you
cdinto a project directory (1) - Configuration: Uses project-local
scripts/.linuxifyfile - Distribution: Copy this repo to any project's
scripts/subdir
(1): assuming you've also added the asdf-direnv mix-in
- macOS (Apple Silicon or Intel)
- Homebrew installed from brew.sh
- zsh shell (standard on modern macOS)
direnv(optional, for project-specific integration)- See the
asdf-direnvrepo for how to installdirenv
- See the
- Clone to home directory:
cd ~
git clone <LINUXIFY-REPO-URL> linuxify- Install utilities:
cd linuxify
./linuxify install- Add to shell configuration:
Add this block manually to both ~/.zshrc and ~/.bashrc (note the LINUXIFY-REPO-URL placeholder):
#######################################
## GNU utilities via linuxify
#######################################
[[ ! -d ~/linuxify ]] && (echo "Cloning linuxify..." && cd ~ && git clone <LINUXIFY-REPO-URL> linuxify && cd ~/linuxify && ./linuxify install)
pushd ~/linuxify >/dev/null && git remote update && git status -uno | grep 'up to date' || (echo "Pulling linuxify updates..." && git pull && ./linuxify install)
. ~/.linuxify
popd >/dev/nullEven if you use zsh, this is needed in ~/.bashrc to support scripts that specify a bash shell e.g. via #!/usr/bin/env bash.
- Open new terminal to activate
Alternative: Modular zshrc approach
If you use a modular ~/.zshrc.d/ setup, you can use the provided auto-updating module instead:
cp support-files/.zshrc.d/050_linuxify.sh ~/.zshrc.d/This module will automatically clone, update, and source linuxify on every new shell session. See support-files/.zshrc.d/050_linuxify.sh for details.
- Copy to project:
# From your project root (using rsync to exclude .git folder)
# Note: no trailing "/" on source means "copy the directory itself" into scripts/
mkdir -p scripts
rsync -av --exclude='.git' /PATH/TO/LINUXIFY/REPO scripts/- Install utilities:
cd scripts/linuxify
./linuxify install- Integrate with
.envrc:
Note: This step is only needed if your project does NOT use the asdf-direnv mix-in. If you're using asdf-direnv, linuxify integration is automatic.
# Add to your project's .envrc (only if NOT already using the asdf-direnv version of .envrc)
if [ -f "scripts/linuxify/.linuxify" ]; then
source scripts/linuxify/.linuxify
fi- Enable
direnv:
direnv allow- Verify installation:
# These should show GNU versions
sed --version # Should show GNU sed
make --version # Should show GNU Make
date --version # Should show GNU coreutils
Core GNU utilities (high impact for script compatibility):
coreutils- 100+ essential utilities (cat, cp, date, ls, mv, etc.)findutils- File finding utilities (find, xargs, locate) with extended featuresdiffutils- File comparison utilities (diff, cmp, sdiff) with more optionsgnu-sed- Stream editor with extended regex supportgnu-tar- Archive utility with enhanced features (NOTE: may lose macOS metadata)gnu-which- Command locatorgrep- Pattern matching with extended featuresgawk- GNU AWK with more functionsmake- Build automation with GNU extensions
Text processing & development tools:
gnu-indent- C code formatterwdiff- Word-based diffed- Line editorless- Pager with more featuresnano- Text editorvim- Enhanced text editoremacs- Text editor
Compression & archive utilities:
gzip- Compression utilityunzip- Archive extraction
Build & compilation tools:
autoconf- Automatic configure script builderm4- Macro processorbison- Parser generatorflex- Fast lexical analyzerbinutils- Binary utilitiesgpatch- Apply patches
System libraries (important for asdf / Python support):
openssl@3- Cryptography and SSL/TLS toolkitreadline- Command line editing librarysqlite- Embedded SQL database enginexz- XZ and LZMA compression libraryzlib- General-purpose compression librarybzip2- Block-sorting compression librarylibffi- Foreign function interface librarytcl-tk- Tool Command Language and Tk GUI toolkit
Network & download tools:
wget- Network downloadercurl- Enhanced HTTP clientnmap- Network scanning and discoveryopenssh- Secure shell
System utilities:
watch- Execute program periodicallyscreen- Terminal multiplexertree- Directory structure visualizationfile-formula- Enhanced file type identificationbash- GNU Bourne-Again Shellperl- Programming languagersync- File synchronization
Security & crypto tools:
gpg- GNU Privacy Guardlibressl- OpenSSL alternative
Version control & development:
git- Version controlgh- GitHub CLI
Database & formatting tools:
libpq- PostgreSQL client toolspgformatter- PostgreSQL formattersqlfluff- SQL linter and formatter
Container & monitoring tools:
ctop- Container monitoring
Available on Intel Macs only:
gdb- GNU Debugger (requires special configuration)
linuxify is designed to work seamlessly with asdf version manager:
- Division of responsibility:
linuxifyhandles system utilities,asdfhandles language-specific tools - Tool prioritization:
asdf-managed tools take precedence over Homebrew versions, in case of overlap - No conflicts: Python and other dev-oriented or language tools are intentionally excluded from
linuxify
See the asdf-direnv mix-in for more information.
Enhanced integration with direnv for project-specific environments:
- Direct sourcing: Project
.envrcfiles can source linuxify configuration directly - Environment variable support: Exports
LDFLAGS,CPPFLAGS,PKG_CONFIG_PATH - Project isolation: Embedded mode works cleanly with
.envrcfiles
See the asdf-direnv mix-in for more information.
- Consistent behavior: Scripts work identically in macOS and Linux containers
- Environment variables: Properly configured build flags for compilation
Tools are prioritized in this order:
asdf-specific paths (e.g., what's specified in.tool-versions)- GNU-specific paths (e.g.,
/opt/homebrew/opt/coreutils/libexec/gnubin) - General Homebrew paths (e.g.,
/opt/homebrew/bin) - System paths (e.g.,
/usr/bin,/bin)
Variables are handled additively to avoid conflicts:
export LDFLAGS="${LDFLAGS:-} -L${BREW_HOME}/opt/flex/lib"
export CPPFLAGS="${CPPFLAGS:-} -I${BREW_HOME}/opt/flex/include"linuxify sets HOMEBREW_NO_AUTO_UPDATE=1 and HOMEBREW_NO_INSTALL_CLEANUP=1 to speed up Homebrew operations. Note: These variables persist in the current terminal session once linuxify loads, even when changing to directories without linuxify. To apply globally across all terminal sessions, add these variables to your ~/.zshrc.
The script automatically detects its operation mode:
- Standalone: When located at
~/linuxify/ - Embedded: When located at
*/scripts/linuxify/ - Unknown: When run from any other location, the script will install tools but won't copy configuration files to standard locations
Command not found after installation:
- Open a new terminal session
- Check that shell configuration was properly updated
- Verify PATH with
echo $PATH | tr ':' '\n' | grep -E '(gnu|homebrew)'
Conflicts with existing tools:
asdftools should take precedence over GNU (if usingasdf)- GNU tools should take precedence over BSD (always)
- Check installation order in shell configuration
- Use
which <command>to verify tool source
Permission errors:
- Ensure Homebrew directories are writable
- Run with appropriate permissions:
./linuxify install
./linuxify install # Install all utilities
./linuxify uninstall # Remove all utilities (keeps current shell if bash)
./linuxify info # Show information about all tools
./linuxify help # Show usage information
Quick test - verify individual tools:
# These should show GNU versions
sed --version # Should show GNU sed
make --version # Should show GNU Make
date --version # Should show GNU coreutilsComprehensive test - verify all utilities at once:
./test-linuxifyThis script checks:
- All utilities are installed and runnable
- GNU tools show "GNU" in their version output
- Commands resolve to Homebrew versions (not macOS BSD versions)
- Libraries are installed via Homebrew
The test provides a clear summary with pass/fail counts and helpful troubleshooting tips.
The linuxify mix-in works seamlessly with other development environment mix-ins. Its compatibility and arms-length integration with asdf-direnv is especially notable. Neither linuxify and asdf-direnv depends on the other, but they were each developed with the other in mind. When you're changing either repo, it's wise to have a look at the other one too, and test them in combination.
asdf-direnv and linuxify both contribute to standardizing tools and versions in the MacOS environment, although different sets of tools. linuxify focuses on general system utilities like git or grep that can usually be used with any project and no specific version requirement other than "pretty recent". asdf-direnv focuses on programming tools like python and node, and also more general tools that are often used in scripts and Makefiles like jq and awscli.
When linuxify is used with the asdf-direnv mix-in:
- Automatic configuration: No manual .envrc configuration needed
- Proper precedence:
asdfversions → GNU tools → System tools - Error handling: Fails fast if linuxify configuration is broken
- Make modules: Standardized makefile capabilities
- Local postgres: Dev-local database automation
- (Additional mix-ins TBD)
For projects not using asdf-direnv, add to .envrc:
# GNU utilities (linuxify mix-in)
if [ -f "scripts/linuxify/.linuxify" ]; then
source scripts/linuxify/.linuxify || {
echo "❌ FATAL: Failed to load linuxify configuration"
return 1
}
fi
When adding a new utility to linuxify, you need to update up to three files:
linuxify- Add the formula to the appropriate section.linuxify- Add PATH/environment variables (if needed)README.md- Document the new utility
Add the Homebrew formula name to the linuxify_formulas array in the appropriate section:
linuxify_formulas=(
# ... existing tools ...
# === YOUR SECTION ===
"your-formula" # Brief description
)Check if the formula is "keg-only" (not symlinked to /opt/homebrew/bin):
brew info your-formula | grep "keg-only"Example 1: Non-keg-only formula (like wget)
Most utilities install to ${BREW_HOME}/bin and are automatically symlinked. These need no changes to .linuxify.
# wget installs to /opt/homebrew/bin/wget - already in PATH
# No .linuxify update neededExample 2: Keg-only formula (like openssl@3)
Keg-only formulas aren't symlinked to avoid conflicts with macOS system libraries. These require .linuxify updates:
# openssl@3 is keg-only - needs explicit PATH and flagsAdd to .linuxify:
# openssl@3 - Cryptography and SSL/TLS toolkit
export PATH="${BREW_HOME}/opt/openssl@3/bin:$PATH"
export LDFLAGS="${LDFLAGS:-} -L${BREW_HOME}/opt/openssl@3/lib"
export CPPFLAGS="${CPPFLAGS:-} -I${BREW_HOME}/opt/openssl@3/include"
export PKG_CONFIG_PATH="${BREW_HOME}/opt/openssl@3/lib/pkgconfig:${PKG_CONFIG_PATH:-}"When to add what:
- PATH: If the formula provides binaries you want to use
- LDFLAGS: If other software needs to link against its libraries
- CPPFLAGS: If other software needs to compile against its headers
- PKG_CONFIG_PATH: If the formula provides
.pcfiles for pkg-config
Special cases requiring .linuxify updates:
Some non-keg-only formulas also need PATH updates if their binaries are in non-standard locations (e.g., coreutils installs to libexec/gnubin to avoid conflicts).
Add the tool to the "Tool inventory" section under the appropriate category with a brief description.
- Test installation:
./linuxify install - Test both modes: Standalone (
~/linuxify/) and embedded (*/scripts/linuxify/) - Verify PATH:
which your-commandshows the Homebrew version - Test builds: If adding libraries, verify software can compile against them
When updating this consolidated version:
- Test both modes: Standalone and embedded operation
- Verify tool lists: Ensure all tools install correctly
- Check integrations: Test with existing
.envrcand shell configs - Update documentation: Keep README current with changes
- Coordinate changes: Keep
linuxifyand.linuxifyin sync
MIT License - see LICENSE file for details.